← Back to Feed CACHED · 2026-10-22 01:25:40 · CACHE_KEY CVE-2026-103119
CVE-2026-103119 · CWE-127 · Disclosed 2026-10-20

Apache Tomcat AJP connector buffer under-read causing cross-request response body leakage

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Like a mailroom accidentally stuffing part of one letter into the next envelope, but only on a locked internal corridor

CVE-2026-103119 is a buffer under-read (CWE-127) in Apache Tomcat 11's AJP (Apache JServ Protocol) connector. When a large non-blocking write occurs, the response body can be incorrectly split: part of one response leaks into the next request on the same persistent AJP connection. The second request typically fails outright, and there is a chance that sensitive data from the first response is exposed to the second requester. Affected versions are Tomcat 11.0.0-M25 through 11.0.26 only — Tomcat 9.x and 10.x branches are *not* affected. The fix landed in 11.0.27 (commit f13748ba).

The vendor CVSS of 9.1 CRITICAL (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H) dramatically overstates the real-world risk. Apache's own qualitative severity rating is Important, not Critical — and they are right. The score assumes unauthenticated network access to the AJP port, but since the Ghostcat fix in 2020 (CVE-2020-1938), Tomcat's AJP connector defaults to localhost-only binding with secretRequired=true. In Tomcat 11, the AJP connector is commented out entirely in the default server.xml. An administrator must actively reconfigure Tomcat to expose AJP externally for this to be remotely exploitable. The actual impact — probabilistic leakage of adjacent response data on a single connection, not arbitrary memory read — is also narrower than C:H suggests. With no PoC, no KEV listing, an EPSS of 0.006, and zero in-the-wild exploitation, this is a medium-priority scheduled patch for the ~31% of Tomcat installations running version 11.

"AJP-only response mixup in Tomcat 11; localhost-bound by default drops real-world risk to medium"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach the AJP port (8009/tcp)

The attacker must establish a TCP connection to Tomcat's AJP connector on port 8009. Since Tomcat 11 defaults to binding AJP on localhost (127.0.0.1 / ::1) with secretRequired=true, this requires either local access to the Tomcat host, adjacent network access where the admin explicitly re-bound AJP to 0.0.0.0, or compromise of the front-end reverse proxy (httpd/nginx) that proxies AJP. Direct internet exposure of AJP is extremely rare post-Ghostcat.
Conditions required:
  • Network path to AJP port 8009
  • AJP connector must be enabled (commented out by default in Tomcat 11)
  • If secretRequired=true (default), attacker needs the configured shared secret
Where this breaks in practice:
  • AJP binds to localhost by default since Tomcat 8.5.51 / 9.0.31 (March 2020)
  • AJP connector is commented out in default Tomcat 11 server.xml
  • Enterprise firewall rules typically block port 8009 from untrusted zones
  • Secret requirement adds an authentication barrier before any AJP communication
Detection/coverage: Shodan/Censys queries for port 8009 can identify externally-exposed AJP endpoints. GreyNoise has no scanning tag for CVE-2026-103119 as of October 2026.
STEP 02

Maintain persistent AJP connection with sequential requests

AJP is a persistent binary protocol where connections are reused across multiple HTTP requests. The attacker must keep the connection alive and issue back-to-back requests so that the response data from request N can bleed into request N+1. This is the fundamental mechanism: the buffer under-read causes the AJP layer to miscalculate response body boundaries during a large non-blocking write operation.
Conditions required:
  • Authenticated AJP session (secret validated)
  • Persistent connection maintained across multiple requests
  • Target application must produce large response bodies using non-blocking I/O (async servlets)
Where this breaks in practice:
  • Most AJP connections are owned by the reverse proxy, not direct attacker connections
  • The non-blocking write path is only triggered for async servlet responses or large streamed payloads
  • Traditional synchronous servlets do not exercise the vulnerable code path
Detection/coverage: AJP request logging via AccessLogValve with AJP-specific patterns would show unusual request sequences. Application-layer anomaly detection may catch malformed follow-up requests.
STEP 03

Trigger buffer under-read via large non-blocking write

The vulnerability fires when a response written through the AJP connector exceeds a buffer boundary during a non-blocking write operation. The under-read causes the connector to send bytes from beyond the intended response buffer into the AJP response stream. These extra bytes become the prefix of the next response on the same connection. The attacker cannot control the content that leaks — it is whatever was adjacent in the response buffer.
Conditions required:
  • Application performs large non-blocking writes (async servlets, streaming responses)
  • Response size must cross a specific buffer boundary threshold that triggers the under-read
Where this breaks in practice:
  • Not all Tomcat applications use non-blocking I/O — many enterprise apps use synchronous servlets
  • The exact buffer boundary condition depends on runtime state and response payload size
  • Attacker has no control over which data leaks or when the condition triggers
Detection/coverage: No public detection signatures (Snort, Suricata, Nuclei) exist for this CVE as of October 2026. WAFs cannot inspect AJP binary protocol traffic.
STEP 04

Harvest leaked response data from subsequent request

If the buffer under-read fires, the next request on the same AJP connection receives a response prefixed with trailing data from the previous response. This could include session tokens, PII, API keys, or other sensitive content depending on the application. However, the attacker receives only the tail end of the prior response body — not arbitrary memory contents. The second request will also typically fail with a malformed response, generating error logs.
Conditions required:
  • Buffer under-read successfully triggered in step 3
  • Leaked data contains something exploitable (session tokens, PII, credentials)
  • Attacker is the party making the follow-up request, not the reverse proxy
Where this breaks in practice:
  • In a standard reverse-proxy architecture, the proxy owns the AJP connection — the proxy's next request gets the leaked data, not an external attacker
  • Leakage is partial and non-deterministic — attacker cannot select target data or target victim
  • Failed second request generates HTTP 5xx errors that are logged and often alerted
Detection/coverage: Application error logs will show malformed responses and HTTP 5xx spikes. Monitoring tools (Datadog, Prometheus, Grafana) would flag the anomalous error rate from failed follow-up requests.
03 · Compensating Control

1
MEDIUM 5.3→IGNORE 0.0
SEVERITY REDUCED
Disable the AJP connector if not in use — Comment out or remove the AJP <Connector> element in server.xml if your architecture uses HTTP/HTTPS proxying (mod_proxy_http, nginx upstream) instead of AJP. This is already the default in Tomcat 11. Verify across all Tomcat 11 hosts within the 365-day noisgate remediation SLA for MEDIUM severity. If AJP is genuinely unused, this single change eliminates 100% of the attack surface.
2
MEDIUM 5.3→LOW 2.5
SEVERITY REDUCED
Verify AJP binds to localhost with secretRequired enabled — If AJP is required (e.g., for mod_jk or mod_proxy_ajp integration), confirm address="127.0.0.1" and secret="<strong-random-value>" are explicitly set on the AJP Connector in server.xml. These are the defaults in Tomcat 11, but confirm they have not been overridden. With localhost binding, only processes on the same host can reach the AJP port, reducing the attack to a local-only post-compromise scenario. Apply within the 365-day remediation window.
3
MEDIUM 5.3→LOW 3.0
SEVERITY REDUCED
Firewall AJP port 8009 from all untrusted networks — Add explicit deny rules on host firewalls (iptables/nftables) and network NGFW to block port 8009 from any source other than the designated reverse proxy IP. This provides defense-in-depth on top of the localhost binding. Deploy within the 365-day noisgate remediation SLA.
4
MEDIUM 5.3→IGNORE 0.0
SEVERITY REDUCED
Upgrade to Tomcat 11.0.27 — The definitive remediation. Version 11.0.27 corrects the buffer under-read in the AJP processor. Schedule within the 365-day noisgate remediation SLA for MEDIUM. No emergency patching cadence is required given zero exploitation activity and zero PoC availability.
What doesn't work
  • WAF rules — WAFs inspect HTTP traffic, not the binary AJP protocol on port 8009. No WAF product can detect or block exploitation of this vulnerability.
  • TLS/mTLS on AJP — AJP does not natively support TLS encryption. Wrapping it in a TLS tunnel (e.g., stunnel) adds transport encryption but does not fix the buffer under-read; the response data still leaks within the encrypted channel between Tomcat and the proxy.
  • Switching NIO/NIO2/APR connector type — The I/O connector implementation (BIO, NIO, NIO2, APR) does not change the AJP protocol handler code path where the buffer under-read occurs. The fix is in the AJP request processor, not the transport I/O layer.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone observed. Not listed on CISA KEV. No reports of active exploitation campaigns as of 2026-10-22. Apache's advisory contains no exploitation warning.
Proof of ConceptNone public. No PoC found on pocindex.io, GitHub (no repos named CVE-2026-103119), ExploitDB, or Nuclei templates. The SecureWithUmer/CVE-2026-PoCs collection does not include this CVE.
EPSS Score0.00609 (~0.6%) — bottom quartile for network-accessible vulnerabilities. Well below any actionable exploitation probability threshold.
KEV StatusNot listed on CISA Known Exploited Vulnerabilities catalog as of 2026-10-22. No due date assigned.
CVSS Vector AnalysisCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H = 9.1. AV:N is misleading: AJP defaults to localhost binding since Ghostcat (2020). AC:L is overstated: the trigger requires non-blocking I/O and specific buffer boundary conditions. A realistic adjusted vector: AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:L (~5.3).
Affected VersionsApache Tomcat 11.0.0-M25 through 11.0.26 only. Tomcat 9.x and 10.x are not affected. This limits the exposed population to ~31% of the Tomcat ecosystem (per OpenLogic Java Trends 2026).
Fixed VersionsTomcat 11.0.27 (commit f13748ba). No backports needed for 9.x or 10.x since those branches are unaffected. Distro packages (RHEL, Ubuntu, Debian) shipping Tomcat 11 should receive the fix in their next update cycle.
Attack Surface / ExposureAJP port 8009 is rarely internet-exposed in modern deployments. Post-Ghostcat defaults changed to localhost-only. Shodan historically showed ~800K-1M Tomcat instances with AJP exposed, but Tomcat 11 AJP exposure is a tiny fraction given the connector is commented out by default.
Disclosure TimelineReported to Apache security team on 2026-09-16. Public advisory released 2026-10-20. Fix available same day in Tomcat 11.0.27. ~34-day coordinated disclosure.
Apache's Own SeverityImportant — notably one tier below the CVSS-derived Critical. Apache's qualitative assessment correctly reflects the AJP-specific attack surface and default-safe configuration that the CVSS vector ignores.

Sources.

  1. Apache Tomcat 11 Security Advisories
  2. ASF Mailing List — CVE-2026-103119 Advisory
  3. SecurityOnline — Apache Tomcat October 2026 Vulnerabilities
  4. Strix.ai — CVE-2026-103119 Fix and Details
  5. GBHackers — Apache Tomcat Security Flaws
  6. GitHub — Fix Commit f13748ba
  7. Offseq Radar — CVE-2026-103119 Threat Analysis
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.3/10)

Why this verdict

  • AJP localhost-only default eliminates the remote attack vector for >95% of deployments. Since Tomcat 9.0.31 (March 2020), the AJP connector binds to localhost with secretRequired=true. Tomcat 11 inherits this and goes further by commenting out the AJP connector entirely in the default server.xml. The CVSS AV:N assumption is factually wrong for the vast majority of real installs. This alone drops the realistic attack vector to AV:L or AV:A, shaving ~2 points off the base score.
  • Non-blocking write trigger adds attack complexity the CVSS ignores. The vulnerability only fires when the application performs a large non-blocking write through the AJP connector using async servlets or streaming responses. Traditional synchronous servlets — still the majority of enterprise Java workloads — are unaffected. This is a runtime-conditional trigger, not a simple packet-to-impact exploit. AC:H is more appropriate than AC:L, reducing the score by ~1 point.
  • Tomcat 11 only — limited installed base. Only ~31% of Tomcat deployments run version 11 (OpenLogic 2026 data). Versions 9.x and 10.x are unaffected. The reachable population is roughly one-third of the Tomcat ecosystem, which itself is ~0.18% of the web server market.
  • No PoC, no exploitation, negligible EPSS. Zero proof-of-concept code exists. EPSS of 0.006 places this in the bottom quartile. No CISA KEV listing. No GreyNoise scanning tags. No threat intel reports. The threat is entirely theoretical as of October 2026.
  • Role multiplier: Tomcat is a typical-role application server, not a canonically high-value component. Tomcat is not a domain controller, hypervisor, identity provider, PAM tool, backup system, or network edge appliance. In its most common high-value deployment (application tier hosting banking/healthcare/PII data), the AJP response leakage *could* expose sensitive per-request data — but the chain requires AJP to be non-default-configured AND reachable AND the app to use async I/O AND the attacker to be the party on the AJP connection (not the reverse proxy). The blast radius is per-connection partial data leakage (host-scope), not domain/fleet/supply-chain compromise. No high-value role scenario produces domain takeover or fleet-scale impact from this flaw alone. The MEDIUM floor holds — no floor override is triggered.

Why not higher?

For this to merit HIGH, the attack path would need to be reliably exploitable over the network without significant preconditions. It is not: AJP defaults to localhost, the connector is off by default in Tomcat 11, a shared secret is required, and the trigger demands non-blocking I/O conditions that most Java applications do not use. Even in a worst-case deployment where AJP is externally exposed with async servlets, the leakage is non-deterministic and per-connection — it does not scale to mass exploitation or fleet compromise. There are zero indicators (no PoC, no KEV, no in-the-wild activity) that would override the friction analysis upward.

Why not lower?

The theoretical impact is real: if an organization has explicitly exposed AJP externally and runs async servlets producing large responses, an attacker on the AJP connection could harvest partial response data including session tokens, PII, or API credentials. The confidentiality impact in that rare scenario is genuine. Apache rated this Important (not Moderate), acknowledging the data leakage potential beyond simple availability disruption. LOW would undercount the risk for the minority of deployments with non-default AJP exposure and would not adequately communicate the need to verify AJP configuration across the fleet.

06 · Verification

Crowdsourced verification payload.

Run on each Tomcat host as any user with read access to $CATALINA_HOME. Invoke: CATALINA_HOME=/opt/tomcat bash check_cve2026103119.sh. No root privileges required. Outputs VULNERABLE (exit 1), PATCHED (exit 0), or UNKNOWN (exit 2). Also reports AJP connector status and bind address if detectable.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/bin/bash
# CVE-2026-103119 Vulnerability Checker
# Detects vulnerable Apache Tomcat 11 versions and AJP connector status
# Usage: CATALINA_HOME=/opt/tomcat bash check_cve2026103119.sh
set -euo pipefail
CH="${CATALINA_HOME:-/opt/tomcat}"
if [ -x "$CH/bin/version.sh" ]; then
  VOUT=$("$CH/bin/version.sh" 2>/dev/null)
elif command -v catalina.sh >/dev/null 2>&1; then
  VOUT=$(catalina.sh version 2>/dev/null)
else
  echo "[!] Tomcat not found. Set CATALINA_HOME."
  echo "UNKNOWN"; exit 2
fi
TV=$(echo "$VOUT" | grep -oP 'Apache Tomcat/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
if [ -z "$TV" ]; then
  echo "[!] Could not parse Tomcat version."
  echo "UNKNOWN"; exit 2
fi
echo "[*] Detected Tomcat version: $TV"
IFS='.' read -r MAJ MIN PAT <<< "$TV"
if [ "$MAJ" -ne 11 ]; then
  echo "[+] Tomcat $TV is not version 11. Not affected by CVE-2026-103119."
  echo "PATCHED"; exit 0
fi
if [ "$MIN" -eq 0 ] && [ "$PAT" -ge 27 ]; then
  echo "[+] Tomcat $TV >= 11.0.27. Patched."
  echo "PATCHED"; exit 0
fi
echo "[!] Tomcat $TV is in the vulnerable range (11.0.0-M25 through 11.0.26)."
SX="$CH/conf/server.xml"
if [ -f "$SX" ]; then
  if grep -qE '^[[:space:]]*<Connector[^>]*protocol[^>]*AJP' "$SX" 2>/dev/null; then
    echo "[!] AJP connector is ENABLED in server.xml."
    BIND=$(grep -A5 'protocol.*AJP' "$SX" | grep -oP 'address="\K[^"]+' | head -1)
    if [ -n "$BIND" ]; then
      echo "[*] AJP bind address: $BIND"
      if echo "$BIND" | grep -qE '^(127\.|::1|localhost)'; then
        echo "[*] AJP bound to loopback. Remote exploitation unlikely."
      else
        echo "[!!] AJP bound to $BIND -- may be externally reachable!"
      fi
    else
      echo "[*] No explicit bind address found. Check defaults."
    fi
  else
    echo "[*] AJP connector not active in server.xml. Code vulnerable but AJP not reachable."
  fi
else
  echo "[*] Cannot read $SX to verify AJP connector status."
fi
echo "VULNERABLE"; exit 1