← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
tenable:102588 · CWE-345 · Disclosed 2017-08-10

Apache Tomcat 8

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

Like a shipping label that forgets to say which customer it belongs to

This Tenable plugin is tracking CVE-2017-7674 in Apache Tomcat's built-in CorsFilter, fixed upstream in 8.0.45. The affected upstream range for this plugin is 8.0.0.RC1 through 8.0.44. The bug is simple: when the CORS filter handled responses, it failed to add Vary: Origin, so caches could treat responses for different origins as interchangeable.

Tenable's MEDIUM rating is technically defensible in a vacuum, but operationally it is still a little generous. Real exploitation requires more than a vulnerable version: the app must actually use Tomcat's CorsFilter, there must be a cache path that reuses the poisoned response across origins, and the attacker still only gets cache poisoning / response mix-up, not server-side code execution. On a 10,000-host estate, this is a config-audit and backlog patch item, not a fire drill.

"This is a misconfiguration-dependent cache poisoning bug, not a Tomcat takeover bug."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find a Tomcat app that actually uses CorsFilter

The attacker starts with a version fingerprint from Nessus, headers, package metadata, or exposed Tomcat content, then has to verify the application is really using Tomcat's built-in org.apache.catalina.filters.CorsFilter. Basic tooling is enough here: curl, Burp Suite Repeater, or a config leak. A version hit alone is not enough to prove exploitability because this flaw lives in a specific filter path, not in Tomcat's core request parser.
Conditions required:
  • Reachable Tomcat-backed application
  • Affected Tomcat version in the vulnerable range
  • Application uses Tomcat's built-in CorsFilter
Where this breaks in practice:
  • Many Tomcat deployments do not enable the built-in CORS filter at all
  • Version-only scanners over-report because they do not validate filter usage
  • Some environments terminate or normalize CORS behavior upstream at a proxy or gateway
Detection/coverage: Remote scanners like Tenable detect version exposure, not whether CorsFilter is enabled. Local config review or filesystem inspection is needed to confirm exposure.
STEP 02

Poison a cache with a crafted Origin request

Using curl or Burp, the attacker sends a request that exercises the CORS path with a chosen Origin header. Because Tomcat omitted Vary: Origin, a downstream browser cache, intermediary proxy, or CDN may store a response without differentiating by origin. The poisoned object can then be reused for a different requester than the application intended.
Conditions required:
  • Response is cacheable somewhere in the path
  • Origin-dependent behavior exists on the response
  • Cache key does not already include Origin
Where this breaks in practice:
  • A lot of modern reverse proxies and CDNs already avoid caching these responses or key more conservatively
  • Private/no-store responses break the chain immediately
  • Dynamic authenticated content is often uncached by policy
Detection/coverage: Most network scanners do not validate cache behavior end-to-end. You need manual replay testing or web proxy instrumentation to prove poisoning.
STEP 03

Wait for a victim or second request path to reuse the poisoned object

The attacker now needs a second request flow that hits the same cache entry from a different origin context. This is where the bug stops being generic and becomes deployment-specific: the vulnerable code path alone does nothing useful unless the cache layer cooperates. Browser behavior, intermediary caching, and app-specific response headers decide whether the poisoned response is ever reused.
Conditions required:
  • Victim traffic or another request path reuses the cached object
  • Cache lifetime and routing keep the object reachable
  • Origin-specific semantics matter to the application
Where this breaks in practice:
  • No shared cache means no practical cross-user impact
  • Short TTLs sharply reduce exploitability
  • Same-origin app patterns often limit any meaningful abuse
Detection/coverage: This usually will not appear as a clean IOC. Look for anomalous Origin combinations, unexpected cache hits, and mismatched Access-Control-* behavior in app and proxy logs.
STEP 04

Exploit the integrity flaw, not the server

Best-case for the attacker is response confusion, cache poisoning, or unintended cross-origin behavior. This is not an RCE chain and not a server compromise primitive by itself. The blast radius stays bounded to whichever endpoints are both CORS-filtered and cache-reused.
Conditions required:
  • Target endpoint has meaningful origin-dependent behavior
  • Poisoned response changes downstream trust or behavior
Where this breaks in practice:
  • Impact is usually low-integrity web behavior, not host compromise
  • No direct path to code execution or privilege escalation
  • Many applications see only nuisance-level impact even if technically vulnerable
Detection/coverage: WAF/EDR products are poor fits here; application telemetry and proxy cache diagnostics are more useful than exploit signatures.
03 · Intelligence Metadata

The supporting signals.

Primary issueCVE-2017-7674: Apache Tomcat CorsFilter omitted Vary: Origin, enabling cache poisoning in some deployments.
In-the-wild statusNo public evidence of active exploitation found in the sources reviewed, and no CISA KEV listing was found.
PoC availabilityNo widely referenced public exploit kit or named GitHub PoC surfaced. That said, this is easy to emulate manually with curl or Burp once you know the app uses CorsFilter.
EPSSLow signal. FIRST publishes daily EPSS, but a secondary Feedly snapshot recorded 0.28% / 64.6th percentile on 2023-12-01, which is consistent with a low-priority web misconfiguration class issue.
KEV statusNot found in CISA's Known Exploited Vulnerabilities catalog in the reviewed catalog/search results.
CVSS and what it meansCVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N = network reachable, no auth required, but user/cache interaction is required and impact is low integrity only.
Affected versionsUpstream affected ranges: Tomcat 8.0.0.RC1-8.0.44, 8.5.0-8.5.15, 9.0.0.M1-9.0.0.M21, and 7.0.41-7.0.78.
Fixed versionsUpstream fixes: 8.0.45, 8.5.16, 9.0.0.M22, 7.0.79. Debian backported fixes for tomcat8 in 8.0.14-1+deb8u11 on Jessie; Ubuntu tracked it via USN-3519-1 for supported releases at the time.
Detection realityThe Nessus plugin is effectively a version-based remote finding. It does not prove that CorsFilter is configured, that a cache exists, or that the cache key ignores Origin.
Lifecycle contextTomcat 8.0.x is end-of-life. That does not make this CVE urgent by itself, but it does mean any surviving 8.0.x host should already be on a migration/removal track.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to LOW (2.8/10)

The decisive downgrade factor is that exploitation depends on a specific optional filter plus a cooperating cache topology; most version-only detections will never be weaponizable. Even when the chain lands, the impact is usually web-response integrity confusion, not host compromise or tenant-wide takeover.

HIGH Exploit preconditions materially narrow real-world exposure
MEDIUM Population of internet-facing apps that both use `CorsFilter` and cache these responses

Why this verdict

  • Version hit ≠ exploit path: this bug only matters if Tomcat's built-in CorsFilter is actually in use.
  • Shared-cache dependency: the attacker needs a browser/proxy/CDN path that stores and reuses the response without varying on Origin.
  • Low blast radius: impact is typically cache poisoning or incorrect CORS behavior on selected endpoints, not server execution or credential-free compromise of the host.
  • No exploitation pressure seen: no KEV listing and no obvious public campaign history push this down further.
  • Scanner inflation: remote tools flag the Tomcat version, but they do not validate filter enablement or cache-key behavior, which is where the real risk lives.

Why not higher?

To rate this MEDIUM or HIGH in enterprise reality, you would want either broader exploitability or stronger impact. Here you have neither: the attacker does not get code execution, and the chain depends on optional app behavior plus a favorable caching setup. That is too much friction for a higher bucket.

Why not lower?

This is not IGNORE because the bug is real and exploitable in the right web architecture. If you are exposing CORS-enabled Tomcat apps behind shared caches or CDNs, the missing Vary: Origin can create genuine response-integrity issues, so the finding deserves validation and cleanup.

05 · Compensating Control

What to do — in priority order.

  1. Verify whether CorsFilter is enabled — Audit conf/web.xml and application WEB-INF/web.xml files first. For a LOW noisgate verdict there is no SLA; treat as backlog hygiene, but do this validation before spending patch effort on every version-only hit.
  2. Add Vary: Origin at the proxy or CDN layer — If the application legitimately varies responses by Origin, enforce Vary: Origin at the reverse proxy, API gateway, or CDN. For LOW, there is no mitigation SLA; treat as backlog hygiene, but this is the cleanest control when upgrades are delayed.
  3. Disable caching on CORS-sensitive responses — Mark affected responses Cache-Control: private, no-store or otherwise exclude them from intermediary caching if origin-sensitive behavior exists. For LOW, this is backlog hygiene rather than emergency work.
  4. Retire Tomcat 8.0.x where possible — Tomcat 8.0.x is EOL, so fold this finding into your broader runtime modernization work. For LOW, there is no SLA; treat as backlog hygiene, but do not leave 8.0.x stranded as a permanent exception.
What doesn't work
  • EDR does not meaningfully help because this is a header/cache semantics issue, not a process-level exploit.
  • MFA is irrelevant because the flaw does not rely on account compromise.
  • TLS only protects the transport; it does not fix incorrect cache variation behavior.
  • Blocking generic file-upload or JSP execution paths does nothing here; this is not the Tomcat PUT-to-JSP RCE class.
06 · Verification

Crowdsourced verification payload.

Run this on the target Tomcat host or against an unpacked Tomcat directory from your gold image/SBOM pipeline. Invoke it as python3 check_cve_2017_7674.py /opt/tomcat and it needs only read access to the Tomcat tree; no admin privileges are required unless the files are locked down.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# check_cve_2017_7674.py
# Determine likely exposure to CVE-2017-7674 on a Tomcat installation.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/error

import os
import re
import sys
from pathlib import Path

AFFECTED = {
    '7.0': ('7.0.41', '7.0.78'),
    '8.0': ('8.0.0.RC1', '8.0.44'),
    '8.5': ('8.5.0', '8.5.15'),
    '9.0': ('9.0.0.M1', '9.0.0.M21'),
}


def parse_version(v):
    v = v.strip()
    m = re.match(r'^(\d+)\.(\d+)\.(\d+)(?:[.-]?(RC|M)(\d+))?$', v, re.IGNORECASE)
    if not m:
        return None
    major, minor, patch, stage, stage_num = m.groups()
    major = int(major)
    minor = int(minor)
    patch = int(patch)
    if not stage:
        stage_rank = 2  # final release > milestone/rc of same numeric version
        stage_num = 0
    else:
        stage = stage.upper()
        stage_rank = 0 if stage == 'M' else 1  # M < RC < final
        stage_num = int(stage_num)
    return (major, minor, patch, stage_rank, stage_num)


def cmp_ver(a, b):
    pa = parse_version(a)
    pb = parse_version(b)
    if pa is None or pb is None:
        raise ValueError(f'Cannot compare versions: {a} vs {b}')
    return (pa > pb) - (pa < pb)


def version_in_range(v, lo, hi):
    return cmp_ver(v, lo) >= 0 and cmp_ver(v, hi) <= 0


def read_text_if_exists(p):
    try:
        return p.read_text(errors='ignore')
    except Exception:
        return None


def detect_version(tomcat_home: Path):
    candidates = [
        tomcat_home / 'RELEASE-NOTES',
        tomcat_home / 'RUNNING.txt',
        tomcat_home / 'bin' / 'catalina.sh',
        tomcat_home / 'lib' / 'catalina.jar',
        tomcat_home / 'lib' / 'tomcat-coyote.jar',
    ]

    # Best effort from text files first
    for p in candidates[:3]:
        txt = read_text_if_exists(p)
        if not txt:
            continue
        for pat in [r'Apache Tomcat Version\s+([0-9][^\s]+)', r'Apache Tomcat/([0-9][^\s]+)', r'\b([0-9]+\.[0-9]+\.[0-9]+(?:[.-]?(?:RC|M)[0-9]+)?)\b']:
            m = re.search(pat, txt, re.IGNORECASE)
            if m:
                return m.group(1)

    # Try manifest from jars without external deps
    import zipfile
    for p in candidates[3:]:
        if not p.exists():
            continue
        try:
            with zipfile.ZipFile(p) as zf:
                for name in ('META-INF/MANIFEST.MF',):
                    try:
                        data = zf.read(name).decode('utf-8', errors='ignore')
                    except Exception:
                        continue
                    for pat in [r'Implementation-Version:\s*([^\s]+)', r'Specification-Version:\s*([^\s]+)']:
                        m = re.search(pat, data, re.IGNORECASE)
                        if m:
                            return m.group(1)
        except Exception:
            pass
    return None


def branch_key(v):
    pv = parse_version(v)
    if not pv:
        return None
    return f'{pv[0]}.{pv[1]}'


def find_corsfilter_usage(tomcat_home: Path):
    hits = []
    search_roots = [tomcat_home / 'conf', tomcat_home / 'webapps']
    needles = [
        'org.apache.catalina.filters.CorsFilter',
        '<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>',
        'CorsFilter'
    ]
    for root in search_roots:
        if not root.exists():
            continue
        for path in root.rglob('*.xml'):
            txt = read_text_if_exists(path)
            if not txt:
                continue
            if any(n in txt for n in needles):
                hits.append(str(path))
    return hits


def main():
    if len(sys.argv) != 2:
        print('UNKNOWN - usage: python3 check_cve_2017_7674.py <TOMCAT_HOME>')
        sys.exit(3)

    tomcat_home = Path(sys.argv[1]).resolve()
    if not tomcat_home.exists() or not tomcat_home.is_dir():
        print(f'UNKNOWN - invalid TOMCAT_HOME: {tomcat_home}')
        sys.exit(3)

    version = detect_version(tomcat_home)
    if not version:
        print('UNKNOWN - could not determine Tomcat version from installation files')
        sys.exit(2)

    key = branch_key(version)
    if key not in AFFECTED:
        print(f'PATCHED - Tomcat version {version} is outside known affected branches for CVE-2017-7674')
        sys.exit(0)

    lo, hi = AFFECTED[key]
    if not version_in_range(version, lo, hi):
        print(f'PATCHED - Tomcat version {version} is not in the vulnerable range {lo}..{hi}')
        sys.exit(0)

    hits = find_corsfilter_usage(tomcat_home)
    if hits:
        sample = '; '.join(hits[:5])
        more = '' if len(hits) <= 5 else f' (+{len(hits)-5} more)'
        print(f'VULNERABLE - Tomcat {version} is in affected range and CorsFilter was found in: {sample}{more}')
        sys.exit(1)

    print(f'UNKNOWN - Tomcat {version} is in affected range, but no CorsFilter reference was found in scanned XML files; verify custom app wiring and proxy cache behavior manually')
    sys.exit(2)


if __name__ == '__main__':
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: separate the real exposures from the version-only noise. First, validate which Tomcat 8.0.x instances actually use CorsFilter; second, for any confirmed CORS-enabled apps behind shared caches, add Vary: Origin or disable caching for those responses in the next normal change window. Because this is a LOW verdict, the noisgate mitigation SLA has no SLA — treat as backlog hygiene, and the noisgate remediation SLA likewise has no SLA — treat as backlog hygiene; fold the upgrade into your Tomcat 8.0.x retirement plan rather than burning an emergency patch cycle on every 102588 hit.

Sources

  1. Tenable Nessus Plugin 102588
  2. Apache Tomcat 8 security page
  3. NVD CVE-2017-7674
  4. Debian security tracker CVE-2017-7674
  5. Ubuntu CVE-2017-7674
  6. CISA Known Exploited Vulnerabilities Catalog
  7. Apache Tomcat 8.0.x EOL notice
  8. Tenable CVE page for CVE-2017-7674
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.