This is a smoke alarm wired to the wrong room
Plugin 83526 flags any Apache Tomcat 7.0.x < 7.0.60 as carrying a bundle of old OpenSSL bugs, especially FREAK (CVE-2015-0204). The practical exposure is much narrower: Tomcat BIO/NIO connectors use JSSE, while the APR/native connector uses OpenSSL. So the hit really matters only when the host loads tcnative / APR, exposes TLS through that path, and is linked against vulnerable OpenSSL (< 0.9.8zd, < 1.0.0p, or < 1.0.1k). On Windows, Tomcat Native 1.1.33 moved bundled binaries to OpenSSL 1.0.1m; that change aligns with the 7.0.60 era.
Tenable's version-only logic materially overstates real risk in modern enterprise fleets. FREAK is a man-in-the-middle downgrade problem, not unauthenticated Tomcat RCE, and even the broader CVE bundle here mostly lands as DoS / crypto weakness inherited from OpenSSL. If your Tomcat estate is standard Java TLS behind a reverse proxy or load balancer, this is usually backlog hygiene, not an emergency.
3 steps from start to impact.
Find a Tomcat node actually using APR/native OpenSSL
tcnative is installed, the APR connector is active or auto-selected, and the linked OpenSSL build is old enough to include the 2015 bug set. The common weaponized validator here is testssl.sh or sslyze for cipher exposure, not an exploit framework.- Tomcat 7.0.x before 7.0.60 is installed
- APR/native (
tcnative) is present and loaded - TLS is exposed through APR/native rather than JSSE
- Most Tomcat deployments use JSSE/NIO and never load APR/native
- Many enterprise deployments terminate TLS on a reverse proxy or ADC instead of Tomcat itself
- The plugin itself states detection is based only on self-reported version
server.xml, and TLS cipher scans are needed to confirm real exposure.Get on path and force a downgrade
RSA_EXPORT. The classic research tooling came from the SmackTLS / FREAK work; modern equivalents are custom MITM tooling or lab PoCs built around TLS downgrade testing. This is a network-adjacent attack, not direct internet-to-host exploitation.- Attacker has man-in-the-middle position or equivalent network control
- Server accepts export-grade RSA ciphers
- Target client stack is susceptible to the downgrade pattern
- Requires prior network foothold, hostile Wi-Fi, malicious proxy, or other path control
- Modern TLS configurations usually disable export ciphers entirely
- Many browsers and clients were patched years ago
Break session confidentiality, not the server
- Successful downgrade to export-grade RSA
- Weak key factoring completed in useful time
- Victim session remains active long enough to exploit
- Blast radius is per intercepted flow, not fleet-wide host takeover
- No persistence or server compromise is obtained from FREAK alone
- Operational payoff is much lower than a typical server-side RCE
The supporting signals.
| In-the-wild status | No current KEV signal. OpenCVE shows KEV no for CVE-2015-0204, and this CVE does not appear in current CISA KEV searches. Historic attention was high in 2015, but this is not a modern mass-exploitation Tomcat item. |
|---|---|
| Proof-of-concept availability | Public PoCs exist. cvefeed reports 67 public GitHub PoC/exploit references for CVE-2015-0204, and the original public research came from the SmackTLS / FREAK work. |
| EPSS | High EPSS, misleading here. Tenable's CVE page shows EPSS 0.91945; cvefeed shows percentile 0.99708 (~99.7th). That reflects broad historical exploit interest in FREAK as a TLS issue, not direct Tomcat-host compromise. |
| KEV status | Not KEV-listed. No CISA KEV entry for CVE-2015-0204 was found, and aggregator metadata reflects KEV no. |
| CVSS vector | CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N from NVD for CVE-2015-0204. That vector already describes a limited confidentiality problem, not host takeover. |
| Affected versions | Nessus flags Tomcat 7.0.x < 7.0.60. In reality, exposure depends on APR/native + OpenSSL; Tomcat docs state BIO/NIO use JSSE while APR/native uses OpenSSL. |
| Fixed versions | OpenSSL fixed FREAK in 0.9.8zd / 1.0.0p / 1.0.1k or later. Tomcat Native changelog shows Windows binaries in 1.1.33 used OpenSSL 1.0.1m; Tomcat 7.0.60+ is the plugin's cut line, but Linux/Unix builds may still rely on distro backports or locally linked OpenSSL. |
| Scanning / exposure data | Internet scanners test cipher posture, not Tomcat version. Shadowserver still publishes an SSL FREAK report for hosts that accept RSA_EXPORT ciphers and rates it LOW. This is a better exposure lens than the plugin's version-only check. |
| Disclosure date | CVE-2015-0204 was published by NVD on 2015-01-08. Nessus plugin 83526 was published on 2015-05-19 and updated 2024-05-06. |
| Reporting researchers | The FREAK research was disclosed by the SmackTLS team, with public attribution to researchers from IMDEA, INRIA, and Microsoft Research. |
noisgate verdict.
The decisive drag on severity is that this is usually not a real Tomcat exposure unless APR/native OpenSSL is actually in play; standard JSSE-based Tomcat deployments are outside the meaningful blast radius. Even when the FREAK path is real, the attacker still needs on-path network control to downgrade TLS, which makes this far less urgent than an internet-reachable server-side exploit.
Why this verdict
- Requires APR/native, not plain Tomcat: Tomcat's own docs say BIO/NIO use
JSSEwhile APR/native usesOpenSSL; that sharply cuts the exposed population from the plugin's blanket7.0.x < 7.0.60claim - Requires on-path attacker: FREAK exploitation assumes a man-in-the-middle position, which implies hostile network control rather than direct unauthenticated internet exploitation
- Impact is session compromise, not host compromise: the practical win is decrypting or tampering with an intercepted TLS session; there is no Tomcat code-execution chain here
- Detection is version-only: Nessus explicitly says it relied on self-reported version and did not exploit or validate the OpenSSL path, which means false-positive volume can be high in large fleets
Why not higher?
A higher rating would make sense for a broadly reachable server-side bug with direct host impact, but this isn't that. The attack chain narrows hard at two points: most Tomcat installs are JSSE-backed, and FREAK still needs a network-adjacent MitM position even when APR/native is present.
Why not lower?
It isn't IGNORE because some older internet-facing Tomcat deployments really did run APR/native with vulnerable OpenSSL, and the plugin's CVE bundle includes more than a cosmetic cipher finding. If you confirm tcnative plus exposed TLS plus weak ciphers, there is still a real, attacker-usable weakness to clean up.
What to do — in priority order.
- Disable export-grade and legacy SSL ciphers — Remove
RSA_EXPORT,SSLv2, andSSLv3from any TLS policy on Tomcat or the upstream reverse proxy. For a LOW verdict there is no mitigation SLA under noisgate; handle this in normal backlog hygiene, but do it first on any internet-facing legacy node you confirm is using APR/native. - Prefer JSSE or terminate TLS upstream — If APR/native is not a hard requirement, move TLS off the OpenSSL-backed connector path or terminate HTTPS on a modern load balancer / proxy. For LOW, there is no mitigation SLA; use your next routine change window.
- Inventory
tcnativeexplicitly — Do not treat every Tomcat< 7.0.60hit as real. Build a targeted inventory of hosts loadinglibtcnative-1/tcnative-1.dll, because only those deserve deeper validation. For LOW, there is no mitigation SLA; this is a validation task to collapse scanner noise.
- Upgrading Tomcat alone on Linux/Unix without checking the linked OpenSSL or distro backport status; the risk lives in the crypto stack, not just the servlet container version string
- Relying on EDR to catch the downgrade; a MitM TLS negotiation problem often leaves little or nothing useful on the Tomcat host
- Assuming internal-only exposure makes it irrelevant; if clients traverse untrusted Wi-Fi, proxies, or hostile network segments, MitM risk can still exist
Crowdsourced verification payload.
Run this on the target Tomcat host with python3 check_tomcat_83526.py /opt/tomcat or py check_tomcat_83526.py C:\Tomcat7. It needs read access to the Tomcat install tree; no admin rights are required unless your Tomcat files are restricted. The script checks Tomcat version, server.xml, APR/native indicators, native library presence, and the local openssl version command when available.
#!/usr/bin/env python3
# check_tomcat_83526.py
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
import os
import re
import sys
import zipfile
import subprocess
import platform
from pathlib import Path
PATCHED = 0
VULNERABLE = 1
UNKNOWN = 2
def read_text(path):
try:
return Path(path).read_text(encoding='utf-8', errors='ignore')
except Exception:
return ''
def parse_version_tuple(v):
nums = re.findall(r'\d+', v or '')
return tuple(int(x) for x in nums[:4]) if nums else tuple()
def version_lt(a, b):
ta = parse_version_tuple(a)
tb = parse_version_tuple(b)
if not ta or not tb:
return None
la = list(ta) + [0] * (len(tb) - len(ta))
lb = list(tb) + [0] * (len(ta) - len(tb))
return tuple(la) < tuple(lb)
def get_tomcat_version(tomcat_home):
candidates = [
Path(tomcat_home) / 'lib' / 'catalina.jar',
Path(tomcat_home) / 'server' / 'lib' / 'catalina.jar',
]
for jar in candidates:
if jar.exists():
try:
with zipfile.ZipFile(jar, 'r') as zf:
data = zf.read('META-INF/MANIFEST.MF').decode('utf-8', errors='ignore')
m = re.search(r'Implementation-Version:\s*([^\r\n]+)', data)
if m:
return m.group(1).strip()
except Exception:
pass
for txt in ['RELEASE-NOTES', 'RUNNING.txt', 'NOTICE']:
data = read_text(Path(tomcat_home) / txt)
m = re.search(r'Apache Tomcat(?:/|\s+)Version\s*[:\-]?\s*([0-9][0-9.]+)', data, re.I)
if m:
return m.group(1)
m = re.search(r'Apache Tomcat/?\s*([0-9][0-9.]+)', data, re.I)
if m:
return m.group(1)
return None
def get_server_xml(tomcat_home):
return read_text(Path(tomcat_home) / 'conf' / 'server.xml')
def apr_indicators(server_xml):
indicators = []
pats = [
r'AprLifecycleListener',
r'Http11AprProtocol',
r'AjpAprProtocol',
r'SSLEngine\s*=\s*"on"',
r'certificateFile\s*=\s*"',
r'certificateKeyFile\s*=\s*"',
]
for p in pats:
if re.search(p, server_xml, re.I):
indicators.append(p)
return indicators
def find_native_libs(tomcat_home):
names = {'tcnative-1.dll', 'libtcnative-1.so', 'libtcnative-1.jnilib', 'libtcnative-1.dylib'}
found = []
for root, dirs, files in os.walk(tomcat_home):
for f in files:
if f.lower() in {n.lower() for n in names}:
found.append(str(Path(root) / f))
return found
def get_openssl_version():
try:
p = subprocess.run(['openssl', 'version'], capture_output=True, text=True, timeout=5)
if p.returncode == 0:
out = (p.stdout or p.stderr).strip()
m = re.search(r'OpenSSL\s+([0-9a-zA-Z.]+)', out)
return m.group(1) if m else out
except Exception:
pass
return None
def openssl_is_vulnerable(v):
if not v:
return None
if re.match(r'^0\.9\.8', v):
return version_lt(v, '0.9.8zd')
if re.match(r'^1\.0\.0', v):
return version_lt(v, '1.0.0p')
if re.match(r'^1\.0\.1', v):
return version_lt(v, '1.0.1k')
return False
def main():
if len(sys.argv) != 2:
print('UNKNOWN - usage: check_tomcat_83526.py <TOMCAT_HOME>')
sys.exit(UNKNOWN)
tomcat_home = sys.argv[1]
if not Path(tomcat_home).exists():
print('UNKNOWN - TOMCAT_HOME not found')
sys.exit(UNKNOWN)
tomcat_ver = get_tomcat_version(tomcat_home)
server_xml = get_server_xml(tomcat_home)
apr_hits = apr_indicators(server_xml)
native_libs = find_native_libs(tomcat_home)
openssl_ver = get_openssl_version()
system_name = platform.system().lower()
apr_in_use_likely = bool(apr_hits or native_libs)
details = {
'tomcat_version': tomcat_ver,
'apr_indicators': apr_hits,
'native_libs': native_libs,
'openssl_version': openssl_ver,
'platform': system_name,
}
# If APR/native is not present, this specific OpenSSL-driven finding is usually not real.
if not apr_in_use_likely:
print(f"PATCHED - no APR/native indicators found; likely JSSE-backed Tomcat. Details={details}")
sys.exit(PATCHED)
# If APR/native is present, inspect OpenSSL version when possible.
if openssl_ver:
vuln = openssl_is_vulnerable(openssl_ver)
if vuln is True:
print(f"VULNERABLE - APR/native present and OpenSSL appears vulnerable ({openssl_ver}). Details={details}")
sys.exit(VULNERABLE)
if vuln is False:
print(f"PATCHED - APR/native present but OpenSSL appears fixed ({openssl_ver}). Details={details}")
sys.exit(PATCHED)
# Windows bundles before Tomcat 7.0.60 are especially suspicious because old tcnative binaries shipped with older OpenSSL.
if system_name.startswith('win') and tomcat_ver:
older = version_lt(tomcat_ver, '7.0.60')
if older is True and native_libs:
print(f"VULNERABLE - Windows Tomcat < 7.0.60 with bundled/native tcnative present; verify OpenSSL immediately. Details={details}")
sys.exit(VULNERABLE)
if older is False:
print(f"PATCHED - Tomcat >= 7.0.60 and native present, but OpenSSL version could not be read. Details={details}")
sys.exit(PATCHED)
print(f"UNKNOWN - APR/native indicators found but OpenSSL version could not be confirmed. Details={details}")
sys.exit(UNKNOWN)
if __name__ == '__main__':
main()
If you remember one thing.
83526 hit. First, validate which Tomcat 7 nodes actually load tcnative / APR and expose TLS from Tomcat instead of a proxy; that is the real cut line. Because this is a LOW verdict, there is no noisgate mitigation SLA and no noisgate remediation SLA beyond normal backlog hygiene, so collapse false positives, fix or retire confirmed APR/native legacy nodes in routine change windows, and only fast-track any internet-facing host you confirm still allows export-grade TLS.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.