Like a bad spare key that only works if two different doors were both installed wrong
This plugin is tracking CVE-2022-42252, an HTTP request smuggling flaw in Apache Tomcat. For the Tomcat 9 branch, the affected range is 9.0.0-M1 through 9.0.67, fixed in 9.0.68 on 2022-10-07. But Tomcat 9 is only exposed if someone explicitly set rejectIllegalHeader="false"; Apache's own advisory says that is not the default for Tomcat 9, even though Tenable's generic plugin text can read like a straight version-only issue.
The vendor-high treatment does not match real enterprise risk for Tomcat 9. This is a classic desync bug with stacked prerequisites: the target must be on a vulnerable version, must have a non-default parser setting, and must sit behind a reverse proxy that also mishandles the malformed Content-Length header. No KEV listing, no strong exploitation signal, and no direct RCE keeps this out of your urgent queue unless you've knowingly weakened header validation.
4 steps from start to impact.
Find a proxied Tomcat edge
http-request-smuggler or smuggler are then used to fingerprint parser discrepancies and keep-alive behavior at the edge.- Unauthenticated remote network reachability to the application
- Tomcat is deployed behind a reverse proxy or load balancer
- Persistent connections are in use across the proxy/backend path
- Many enterprises terminate at modern proxies that normalize or reject malformed headers before Tomcat sees them
- Direct-to-Tomcat deployments are common internally but then this specific desync precondition disappears
- WAF/CDN/LB stacks often collapse connections in ways that kill smuggling reliability
Need the non-default Tomcat 9 parser setting
rejectIllegalHeader=false so Tomcat will accept an invalid Content-Length header that it should otherwise reject. Apache's Tomcat 9 advisory explicitly says this is not the default, which is the single biggest reason this CVE is over-scored in generic patch reports.- Tomcat 9.0.0-M1 to 9.0.67
rejectIllegalHeader="false"configured on the relevant connector
- This is a manual hardening regression, not the Tomcat 9 default state
- Many estates inherited scanner findings from package versions without validating connector config
- Containers and vendor appliances may backport fixes or keep the safe default even when banner versions look old
conf/server.xml and connector attributes; remote scanners usually miss this config prerequisite.Require a second parser mistake in the proxy
Burp HTTP Request Smuggler and smuggler automate CL/TE-style and parser-differential probing to test that mismatch.- Reverse proxy forwards malformed requests instead of rejecting them
- Backend connection reuse allows desynchronization to matter
- This is an architectural dependency outside Tomcat itself
- Different proxies, CDN layers, and service meshes often sanitize away the malformed header
- Even 'vulnerable' stacks can be inconsistent and unreliable to weaponize in production
Turn desync into useful impact
- A meaningful target route exists behind the proxy
- The application behavior is exploitable via desynchronized requests
- Many apps offer no easy cache-poisoning or auth-bypass primitive even when desync exists
- Impact often depends on endpoint-specific quirks rather than the Tomcat bug alone
- Operationally, exploitation is less turnkey than an unauthenticated RCE
rejectIllegalHeader="false" and revert to the safe default unless there is a documented business dependency. For a LOW verdict there is no SLA; treat this as backlog hygiene, but validate internet-facing and shared-platform instances first.Content-Length requests die at the edge. This breaks the second prerequisite in the chain; for LOW, fold it into normal hardening work rather than emergency change.- A version-only exception without checking config does not solve the problem; you need to know whether
rejectIllegalHeader=falsewas set. - A generic WAF is not a dependable fix for request smuggling because the issue is parser disagreement between layers, not just a bad URL or signature match.
- Hiding banners or changing server headers does nothing; this is about request parsing behavior, not product fingerprinting.
The supporting signals.
| Primary CVE | CVE-2022-42252 — Apache Tomcat request smuggling via malformed Content-Length |
|---|---|
| In-the-wild status | No KEV listing found in CISA's Known Exploited Vulnerabilities Catalog, and I found no authoritative active-exploitation bulletin tied to this CVE. |
| Proof-of-concept availability | Yes, generic tooling exists: PortSwigger's HTTP Request Smuggler and smuggler-style tooling are enough to test/weaponize desync conditions, even without a CVE-specific exploit. |
| EPSS | OpenCVE currently shows EPSS 0.00157 for CVE-2022-42252, which is a very low exploitation-likelihood signal. |
| KEV status and dates | Not in KEV as of the current CISA catalog page. Public disclosure was 2022-10-31 per Apache; NVD published the record on 2022-11-01. |
| CVSS and what it misses | NVD/CISA ADP score it 7.5 HIGH with CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N. That base score treats the bug like a broadly reachable network parser flaw, but it does not price in the Tomcat 9 non-default config and the required proxy-side parser failure. |
| Affected versions | Apache states Tomcat 9.0.0-M1 to 9.0.67 are affected, but only when rejectIllegalHeader=false. Tenable plugin 166906 flags the version range broadly. |
| Fixed versions and backports | Upstream fix is 9.0.68. Ubuntu tracks fixes via backports rather than upstream version strings, including USN-6880-1, so package version alone must be interpreted in distro context. |
| Exposure/scanning reality | Internet-wide exposure is hard to count meaningfully because exploitability depends on a proxy/backend pair plus a Tomcat config flag. In practice, version-only scanning overcounts; this is more of a configuration-validation problem than a pure inventory problem. |
| Reporter / disclosure | Apache Tomcat says the issue was reported to the Tomcat Security Team on 2022-09-29 and made public on 2022-10-31 in the Tomcat 9 security page. |
Sources.
Why this verdict
- Non-default on Tomcat 9: Apache says
rejectIllegalHeader=falseis *not* the default for Tomcat 9, so the scanner is flagging many hosts that are version-matched but not actually exposed. - Posture depends on architecture: the bug only matters when a reverse proxy in front of Tomcat also accepts the malformed header and desynchronizes request boundaries.
- Impact is indirect: this is request smuggling with integrity-side effects like cache poisoning or WAF bypass, not a clean unauthenticated RCE primitive.
Why not higher?
There is no credible evidence here of mass exploitation, no KEV listing, and no default-vulnerable state for Tomcat 9. Real exploitability requires two separate parser mistakes across two layers of the stack, which sharply narrows the reachable population.
Why not lower?
I did not push this to IGNORE because some enterprises really do run Tomcat behind proxies with compatibility-driven connector tweaks, and when the chain lines up an unauthenticated attacker can still cause meaningful request-routing and cache integrity problems. It merits validation and cleanup; it just does not merit panic.
Crowdsourced verification payload.
Run this on the target Tomcat host or inside the container image with read access to the Tomcat install and conf/. Invoke it as python3 check_tomcat_cve_2022_42252.py /opt/tomcat or point it at %CATALINA_HOME% on Windows. No admin rights are required if the user can read the Tomcat directory tree.
#!/usr/bin/env python3
# check_tomcat_cve_2022_42252.py
# Detects Tomcat 9 exposure to CVE-2022-42252 based on version + connector config.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
import os
import re
import sys
import zipfile
from pathlib import Path
AFFECTED_FIXED = (9, 0, 68)
def parse_version_string(s):
if not s:
return None
m = re.search(r'(\d+)\.(\d+)\.(\d+)', s)
if not m:
return None
return tuple(int(x) for x in m.groups())
def find_tomcat_version(base):
# 1) RELEASE-NOTES / RUNNING.txt
for rel in ['RELEASE-NOTES', 'RUNNING.txt', 'RELEASE-NOTES.txt']:
p = base / rel
if p.is_file():
try:
txt = p.read_text(errors='ignore')
v = parse_version_string(txt)
if v:
return v, f'{p}'
except Exception:
pass
# 2) catalina.jar -> ServerInfo.properties
jar = base / 'lib' / 'catalina.jar'
if jar.is_file():
try:
with zipfile.ZipFile(jar, 'r') as zf:
for name in [
'org/apache/catalina/util/ServerInfo.properties',
'org/apache/catalina/util/ServerInfo.properties.default'
]:
if name in zf.namelist():
data = zf.read(name).decode('utf-8', errors='ignore')
for line in data.splitlines():
if line.strip().startswith('server.number='):
v = parse_version_string(line.split('=', 1)[1].strip())
if v:
return v, f'{jar}!{name}'
except Exception:
pass
return None, None
def is_tomcat9(v):
return v is not None and len(v) == 3 and v[0] == 9
def scan_reject_illegal_header_false(base):
hits = []
conf = base / 'conf'
if not conf.exists():
return hits
for p in conf.rglob('*.xml'):
try:
txt = p.read_text(errors='ignore')
except Exception:
continue
if re.search(r'rejectIllegalHeader\s*=\s*["\']false["\']', txt, re.IGNORECASE):
hits.append(str(p))
return hits
def main():
arg = None
if len(sys.argv) > 1:
arg = sys.argv[1]
else:
arg = os.environ.get('CATALINA_BASE') or os.environ.get('CATALINA_HOME') or '.'
base = Path(arg).expanduser().resolve()
if not base.exists():
print(f'UNKNOWN - Path not found: {base}')
sys.exit(2)
version, source = find_tomcat_version(base)
if version is None:
print(f'UNKNOWN - Could not determine Tomcat version under {base}')
sys.exit(2)
if not is_tomcat9(version):
print(f'UNKNOWN - Detected Tomcat version {version[0]}.{version[1]}.{version[2]} (script is scoped to Tomcat 9 / plugin 166906)')
sys.exit(2)
hits = scan_reject_illegal_header_false(base)
ver_str = f'{version[0]}.{version[1]}.{version[2]}'
if version >= AFFECTED_FIXED:
print(f'PATCHED - Tomcat {ver_str} >= 9.0.68 (source: {source})')
sys.exit(0)
# For Tomcat 9, Apache states the vulnerable setting is non-default.
if hits:
print(f'VULNERABLE - Tomcat {ver_str} < 9.0.68 and rejectIllegalHeader="false" found in: {", ".join(hits)}')
sys.exit(1)
else:
print(f'PATCHED - Tomcat {ver_str} < 9.0.68 but no rejectIllegalHeader="false" found under {base}/conf; Tomcat 9 is not vulnerable by default')
sys.exit(0)
if __name__ == '__main__':
main()