← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-8711 · CWE-122 · Disclosed 2026-05-19

NGINX JavaScript has a vulnerability when the js_fetch_proxy directive is configured with at least one…

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is less a front-door master key than a brittle custom side hatch only some admins accidentally wired to user input

CVE-2026-8711 is a heap-based buffer overflow in NGINX JavaScript (njs), not core NGINX request handling. It affects njs 0.9.4 through 0.9.8, the range where js_fetch_proxy exists and can accept variables, and only becomes reachable when three things line up at once: njs is deployed, a location actually invokes ngx.fetch(), and js_fetch_proxy contains at least one client-controlled NGINX variable such as $http_*, $arg_*, or $cookie_*. The public descriptions say the normal consequence is a worker-process heap overflow leading to a restart; code execution is only described for systems with ASLR disabled or where ASLR can otherwise be bypassed.

Paragraph-for-paragraph, the vendor's 8.1/HIGH score is defensible in a lab because the bug is remotely triggerable and memory corruption is real. In enterprise fleets, though, the score overstates the reachable population: this is a configuration-gated edge case inside a less-common njs feature path, and the realistic outcome is usually availability impact, not turnkey RCE. NGINX's own njs security page rates it medium, and that better matches the field reality.

"High on paper, medium in enterprise reality: this needs a rare njs proxy misconfig and usually only buys a worker crash."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find an exposed njs-backed endpoint

The attacker first needs an HTTP endpoint whose NGINX location runs JavaScript and performs outbound ngx.fetch() calls. In practice this is usually a custom auth, API glue, or content handler, discovered with ordinary tooling like curl, Burp Repeater, or ffuf by observing dynamic behavior and response patterns rather than a banner-based CVE fingerprint.
Conditions required:
  • The target exposes HTTP(S) to the attacker
  • NGINX has the njs module installed and enabled
  • A location actually invokes ngx.fetch() in its JS handler
Where this breaks in practice:
  • Most NGINX estates do not use njs at all
  • Even among njs users, many locations never call ngx.fetch()
  • Internet scanners cannot reliably tell from the outside whether this code path exists
Detection/coverage: Commodity vuln scanners have weak coverage here because the condition is configuration- and code-path-specific, not just version-specific.
STEP 02

Reach the unsafe proxy directive path

Next, the attacker needs js_fetch_proxy to be configured with at least one client-controlled variable such as a header, cookie, or query argument. This is the real choke point: the bug is not 'njs present' but 'njs present + outbound fetch + proxy URL partly derived from attacker input'.
Conditions required:
  • js_fetch_proxy is configured in the active context
  • Its value includes $http_*, $arg_*, $cookie_*, or equivalent client-derived data
  • The request is routed into that exact location
Where this breaks in practice:
  • Well-run teams rarely build proxy targets directly from user input
  • Many configs hardcode the proxy or leave js_fetch_proxy unset
  • Change reviews, linting, and staged config testing often catch obviously dangerous variable use
Detection/coverage: Config review catches this better than network scanning. nginx -T plus grep is more useful than Nessus here.
STEP 03

Trigger the heap overflow

With the path found, the attacker sends crafted HTTP requests using ordinary tools like curl or Burp Intruder to manipulate the client-controlled variable value until the worker hits the vulnerable processing path. The public record describes the typical result as a heap buffer overflow in the worker process leading to a restart, so the attacker can aim for repeated crashes and degraded service.
Conditions required:
  • The attacker can repeatedly send crafted requests to the vulnerable location
  • The vulnerable njs version is 0.9.4-0.9.8
Where this breaks in practice:
  • Payload shaping is non-trivial; vendor CVSS already marks this as AC:H
  • Modern process supervision causes workers to restart, limiting persistence of the fault
  • Load-balanced fleets dilute impact unless many nodes share the same bad config
Detection/coverage: Look for NGINX worker crashes/restarts, error-log anomalies around njs fetch handling, and repeated malformed requests to JS-backed locations.
STEP 04

Escalate from DoS to code execution

Turning the crash into code execution is a separate problem. The public advisories state RCE is possible when ASLR is disabled, or if the attacker can otherwise bypass ASLR, which is a materially narrower and less common condition than the base crash scenario.
Conditions required:
  • ASLR is disabled, weak, or bypassable
  • The attacker has exploit development maturity beyond a simple crash trigger
Where this breaks in practice:
  • ASLR-off production Linux servers are uncommon in mature estates
  • Exploit reliability drops sharply across distro, allocator, and build differences
  • No primary-source evidence reviewed here shows broad in-the-wild weaponization
Detection/coverage: EDR and crash telemetry may catch post-crash anomalies, but there is no broadly published, signature-stable exploit pattern to key on from primary sources.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo verified active exploitation found in the primary sources reviewed, and not listed in CISA KEV as of 2026-05-29.
Proof-of-concept availabilityNo authoritative public PoC or exploit repo was identified in the primary sources reviewed. Treat exploit availability as unconfirmed, not impossible.
EPSS0.00096 from the user-supplied intel, which is consistent with very low near-term exploitation probability. I did not independently verify the percentile.
KEV statusNo. Absence from KEV removes a major urgency amplifier.
CVSS and what it meansCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H says remote, unauthenticated, no user interaction, but importantly high attack complexity. That AC:H matters here because the vulnerable state depends on a narrow config pattern.
Affected versionsnjs 0.9.4-0.9.8 are vulnerable according to NGINX njs security and change notes. The issue was introduced with commit dea83189.
Fixed versionsUpstream fixed in njs 0.9.9+. Debian tracker shows libnginx-mod-js bookworm and trixie as not affected, while sid/forky is fixed in 0.9.9-1.
Exposure / scanning realityInference from docs: internet census is poor for this CVE. Shodan/Censys-style bannering can tell you a server is NGINX, but not whether njs is enabled, whether a location calls ngx.fetch(), or whether js_fetch_proxy contains client-derived variables.
Disclosure date2026-05-19.
Reporter / researcherPublic sources reviewed do not clearly credit an external discoverer. Treat reporter attribution as not publicly disclosed unless your support portal advisory says otherwise.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.9/10)

The decisive factor is reachability friction: an attacker only gets a shot if the estate runs njs, uses ngx.fetch(), and wires js_fetch_proxy to client-controlled variables. That collapses the exposed population far below generic internet-facing NGINX and makes the usual outcome worker restart/DoS, not broad reliable RCE.

HIGH Version range and vulnerable configuration preconditions
MEDIUM Real-world severity downgrade from vendor 8.1 to MEDIUM
LOW Public exploit availability and external exposure census

Why this verdict

  • Config dependency cuts the blast radius hard: start from vendor 8.1, then score down because the CVE is only reachable when njs is enabled, a location actually uses ngx.fetch(), and js_fetch_proxy includes client-controlled variables. That is a narrow subset of NGINX deployments, not the whole edge fleet.
  • The attacker does not get clean RCE by default: the public descriptions say the common result is worker restart. Code execution needs ASLR disabled or bypassed, which is a significant real-world limiter.
  • Threat telemetry is cold: user-supplied EPSS 0.00096, no KEV listing, and no verified primary-source PoC or campaign evidence mean there is no pressure to treat this like an active perimeter emergency.

Why not higher?

It is not higher because this is not a simple 'version installed equals remotely exploitable' bug. The chain stacks multiple prerequisites, and the most serious impact case depends on weakened memory-randomization conditions that many modern Linux deployments do not have.

Why not lower?

It is not lower because the attacker can be unauthenticated and remote, and many affected hosts will be edge-facing if they use this feature at all. Even if exploitation usually lands as crash/restart rather than RCE, repeated worker disruption on an internet-facing gateway is still operationally meaningful.

05 · Compensating Control

What to do — in priority order.

  1. Audit js_fetch_proxy now — Search every NGINX config for js_fetch_proxy and flag any use of $http_*, $arg_*, $cookie_*, or other client-derived variables. Because this is a MEDIUM verdict there is no mitigation SLA — implement during the next normal change window, but do the audit immediately so you know whether you even have exposure.
  2. Freeze user-derived proxy targets — Replace variable-driven proxy URLs with static values or tightly mapped internal variables where possible. This removes the vulnerable input path entirely; for MEDIUM, there is no mitigation SLA — go straight to a routine risk-reduction change.
  3. Keep ASLR enforced — Verify kernel ASLR remains enabled on Linux hosts so the bug stays in the more-likely DoS bucket instead of drifting toward code-execution conditions. This is hygiene, not an emergency measure: no mitigation SLA for this severity.
  4. Watch for worker churn — Instrument NGINX error logs, service restart counts, and container crash loops around JS-backed locations that call ngx.fetch(). There is no mitigation SLA for MEDIUM, but fast visibility lets you spot abuse before the patch rolls through.
What doesn't work
  • A generic WAF rule is not enough: the dangerous input can ride in ordinary headers, cookies, or query parameters, and the flaw lives in NGINX's own njs fetch/proxy handling path.
  • MFA or app-layer auth does not help when the vulnerable location is reachable pre-auth at the reverse proxy layer.
  • Hiding version banners does nothing because exploitability depends on local config and code path, not just what Server: says.
06 · Verification

Crowdsourced verification payload.

Run this on the target NGINX host with read access to the live configuration and permission to execute nginx -T; root or sudo is recommended. Save as check-cve-2026-8711.sh and run sudo bash ./check-cve-2026-8711.sh /etc/nginx — it will inspect package versions plus config usage and print VULNERABLE, PATCHED, or UNKNOWN.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check-cve-2026-8711.sh
# Detect likely exposure to CVE-2026-8711 on Linux NGINX hosts.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -u

CONF_ROOT="${1:-/etc/nginx}"
TMP_CONF=""
STATUS="UNKNOWN"
DETAILS=()

cleanup() {
  [ -n "$TMP_CONF" ] && [ -f "$TMP_CONF" ] && rm -f "$TMP_CONF"
}
trap cleanup EXIT

add_detail() {
  DETAILS+=("$1")
}

ver_ge() {
  [ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
}

ver_lt() {
  [ "$1" != "$2" ] && [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]
}

is_vuln_njs_version() {
  local v="$1"
  ver_ge "$v" "0.9.4" && ver_lt "$v" "0.9.9"
}

get_pkg_version() {
  local p=""
  if command -v dpkg-query >/dev/null 2>&1; then
    for pkg in libnginx-mod-js njs nginx-module-njs; do
      p=$(dpkg-query -W -f='${Version}\n' "$pkg" 2>/dev/null | head -n1)
      if [ -n "$p" ]; then
        echo "$p" | sed 's/-.*$//'
        return 0
      fi
    done
  fi

  if command -v rpm >/dev/null 2>&1; then
    for pkg in nginx-module-njs njs; do
      p=$(rpm -q --qf '%{VERSION}\n' "$pkg" 2>/dev/null | head -n1)
      if [ -n "$p" ] && [[ "$p" != *"not installed"* ]]; then
        echo "$p"
        return 0
      fi
    done
  fi

  if command -v apk >/dev/null 2>&1; then
    for pkg in nginx-mod-http-js njs; do
      p=$(apk info -e "$pkg" >/dev/null 2>&1 && apk info -v "$pkg" 2>/dev/null | head -n1)
      if [ -n "$p" ]; then
        echo "$p" | sed 's/^.*-\([0-9].*\)$/\1/' | sed 's/-r[0-9].*$//'
        return 0
      fi
    done
  fi

  return 1
}

get_live_config() {
  if command -v nginx >/dev/null 2>&1; then
    TMP_CONF=$(mktemp)
    if nginx -T >"$TMP_CONF" 2>/dev/null; then
      echo "$TMP_CONF"
      return 0
    fi
  fi

  if [ -d "$CONF_ROOT" ]; then
    TMP_CONF=$(mktemp)
    find "$CONF_ROOT" -type f \( -name '*.conf' -o -name 'nginx.conf' -o -name '*.js' \) -readable -print0 2>/dev/null \
      | xargs -0 cat 2>/dev/null >"$TMP_CONF"
    if [ -s "$TMP_CONF" ]; then
      echo "$TMP_CONF"
      return 0
    fi
  fi

  return 1
}

CONFIG_FILE=""
if ! CONFIG_FILE=$(get_live_config); then
  echo "UNKNOWN - unable to read nginx configuration via 'nginx -T' or $CONF_ROOT"
  exit 2
fi

NJS_VERSION=""
if NJS_VERSION=$(get_pkg_version); then
  add_detail "njs/libnginx-mod-js version: $NJS_VERSION"
else
  add_detail "njs package version: undetermined"
fi

HAS_JS_IMPORT=0
HAS_FETCH=0
HAS_PROXY=0
HAS_CLIENT_VAR_PROXY=0

if grep -Eq '^[[:space:]]*js_import[[:space:]]+' "$CONFIG_FILE"; then
  HAS_JS_IMPORT=1
  add_detail "js_import present"
fi

if grep -Eq 'ngx\.fetch[[:space:]]*\(' "$CONFIG_FILE"; then
  HAS_FETCH=1
  add_detail "ngx.fetch() usage present"
fi

if grep -Eq '^[[:space:]]*js_fetch_proxy[[:space:]]+' "$CONFIG_FILE"; then
  HAS_PROXY=1
  add_detail "js_fetch_proxy present"
fi

if grep -E '^[[:space:]]*js_fetch_proxy[[:space:]]+.*\$(http_|arg_|cookie_)' "$CONFIG_FILE" >/dev/null 2>&1; then
  HAS_CLIENT_VAR_PROXY=1
  add_detail "js_fetch_proxy uses client-controlled variable(s)"
fi

if [ "$HAS_PROXY" -eq 0 ] || [ "$HAS_FETCH" -eq 0 ]; then
  echo "PATCHED - vulnerable code path not detected (${DETAILS[*]})"
  exit 0
fi

if [ "$HAS_CLIENT_VAR_PROXY" -eq 0 ]; then
  echo "PATCHED - js_fetch_proxy found, but not with obvious client-controlled variables (${DETAILS[*]})"
  exit 0
fi

if [ -n "$NJS_VERSION" ]; then
  if is_vuln_njs_version "$NJS_VERSION"; then
    echo "VULNERABLE - vulnerable njs version and risky config pattern detected (${DETAILS[*]})"
    exit 1
  else
    echo "PATCHED - risky config pattern detected, but installed njs version is outside 0.9.4-0.9.8 (${DETAILS[*]})"
    exit 0
  fi
fi

echo "UNKNOWN - risky config pattern detected, but njs package version could not be determined (${DETAILS[*]})"
exit 2
07 · Bottom Line

If you remember one thing.

TL;DR
By Monday morning, do not treat this like a fleet-wide edge panic; treat it like a targeted config hunt. Inventory hosts running njs/libnginx-mod-js, grep for js_fetch_proxy + client-controlled variables + ngx.fetch(), and if you find that pattern on internet-facing gateways, clean up the config or upgrade those nodes first; for a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window under the noisgate remediation SLA, but exposed edge exceptions deserve earlier handling in normal change control.

Sources

  1. NGINX njs security page
  2. NGINX njs 0.9.9 changes
  3. NGINX JavaScript module reference (`js_fetch_proxy`)
  4. NGINX release news
  5. NVD record for CVE-2026-8711
  6. Debian security tracker for CVE-2026-8711
  7. Amazon Linux CVE page
  8. CISA Known Exploited Vulnerabilities Catalog
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.