Unlike CVE-2026-43512, where the digest hashes matched but the bypass never materialised, this time the exploit works. The question is not whether the vulnerability is real — it is — but whether the configuration required to trigger it exists in the wild.
Primer: Security Constraints in Tomcat
Before analysing the bug, it is worth understanding how Tomcat evaluates security constraints, because the vulnerability is entirely a function of how web.xml is written.
Defining Protected Resources
Security constraints in Tomcat are declared in web.xml via <security-constraint> elements. Each constraint contains one or more <web-resource-collection> blocks that define which URLs and HTTP methods are in scope, and an <auth-constraint> that specifies which roles are allowed access.
A minimal example:
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/protected/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
When no <http-method> is specified inside <web-resource-collection>, the constraint applies to all HTTP methods. This is by far the most common pattern in production deployments.
The Servlet specification also allows per-method constraints:
<web-resource-collection>
<url-pattern>*.html</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
This means “apply this constraint only to GET requests on *.html”. All other methods on that pattern are unconstrained by default unless explicitly listed elsewhere.
How findSecurityConstraints() Works
When a request arrives, AuthenticatorBase calls RealmBase.findSecurityConstraints(request, context) to determine which constraints apply. The method iterates every configured SecurityConstraint, and for each one walks its SecurityCollection[] looking for a URL pattern that matches the request URI. If a matching collection is found and its method list includes the request method, the constraint is added to the result set.
If findSecurityConstraints() returns null or an empty set, AuthenticatorBase logs Not subject to any constraint and serves the request without invoking authenticate(). This is exactly the line that appears in the verbose log for the bypassed POST request.
The Vulnerability in findSecurityConstraints()
The bug is a scoping error. In the pre-fix code, two variables — matched and pos — were declared outside the per-collection loop:
// RealmBase.java — vulnerable (≤ 11.0.21)
boolean matched = false;
int pos = -1;
for (int j = 0; j < collection.length; j++) {
String[] patterns = collection[j].findPatterns();
for (int k = 0; k < patterns.length && !matched; k++) {
String pattern = patterns[k];
if (pattern.startsWith("*.")) {
if (pattern.regionMatches(1, uri, dot, uri.length() - dot)) {
matched = true;
pos = j; // ← frozen to the first matching collection
}
}
}
}
if (matched) {
found = true;
if (collection[pos].findMethod(method)) { // ← uses pos, not j
results.add(constraints[i]);
}
}
Consider a web.xml with two collections under the same constraint:
collection[0]— pattern*.html, methodGETcollection[1]— pattern*.html, methodPOST
When a POST request arrives for /protected/secret.html:
- The loop reaches
collection[0], pattern*.htmlmatches →matched = true,pos = 0 - Because
!matchedis nowfalse, the innerkloop exits immediately —collection[1]is never evaluated for pattern matching - After the loop:
matchedistrue, sofindMethod(method)is called oncollection[pos]=collection[0] collection[0].findMethod("POST")returnsfalse— collection[0] only declaresGET- No constraint is added to
resultsfor the POST request AuthenticatorBaseconcludes: Not subject to any constraint → serves the response without authentication
The Fix
The fix (commit 276087d) moves matched inside the loop and replaces collection[pos] with collection[j]:
// RealmBase.java — patched (≥ 11.0.22)
for (int j = 0; j < collection.length; j++) {
boolean matched = false; // ← scoped to this iteration
// ... pattern matching sets matched = true ...
if (matched) {
found = true;
if (collection[j].findMethod(method)) { // ← j, not pos
if (results == null) {
results = new ArrayList<>();
}
results.add(constraints[i]);
}
}
}
Each collection is now evaluated independently. For the POST request, collection[1] correctly sets matched = true, collection[1].findMethod("POST") returns true, and the constraint is added to the result set.
The PoC Environment
The reproduction environment uses Tomcat 11.0.0-M1 with the exact web.xml shape that triggers the bug. The full source is at github.com/covepseng/cve-2026-43515-poc.
podman build -t tomcat-cve-2026-43515 .
podman run -d --name tomcat-vuln -p 8080:8080 tomcat-cve-2026-43515
cd exploit
go run exploit.go \
-target http://localhost:8080 \
-path /protected/secret.html \
-username validuser \
-password s3cret!
The exploit sends three probes and prints a verdict. No external Go dependencies.
Probe 1 — GET without credentials
The GET method is declared in collection[0]. Even on vulnerable Tomcat, this collection is evaluated correctly — GET is blocked.
[1] GET (no credentials) → HTTP 401 ← ✓ constraint enforced as expected
Probe 2 — POST without credentials
The POST method is declared in collection[1]. On vulnerable Tomcat this collection is never reached. findSecurityConstraints() returns no constraint for POST, and the request is served without authentication.
[2] POST (no credentials) → HTTP 200 ← ✗ BYPASS CONFIRMED
Probe 3 — GET with valid credentials
Sanity check: authenticate as validuser with role admin to confirm the resource is reachable and the credential set is correct.
[3] GET (with credentials) → HTTP 200 ← ✓ authenticated access granted
What Tomcat’s Verbose Log Shows
Enabling FINE-level logging confirms the mechanism at the server side:
// GET — constraint correctly triggers authentication
AuthenticatorBase.invoke Calling authenticate()
AuthenticatorBase.invoke Failed authenticate() test → 401
// POST — findSecurityConstraints() returns nothing
AuthenticatorBase.invoke Not subject to any constraint → 200
The phrase “Not subject to any constraint” is the server confirming that findSecurityConstraints() returned an empty result for the POST request — not that the constraint was evaluated and passed, but that it was never found at all.
Why the Configuration is Uncommon
The bypass only triggers under a specific web.xml pattern that is valid for the Servlet specification but rarely seen in practice: a single <security-constraint> with multiple <web-resource-collection> blocks sharing the same extension pattern, each declaring a different HTTP method.
Most deployments use one of two safer patterns instead:
<!-- Pattern A: no <http-method> — protects all methods (most common) -->
<web-resource-collection>
<url-pattern>/protected/*</url-pattern>
</web-resource-collection>
<!-- Pattern B: separate <security-constraint> per method — also immune -->
<security-constraint>
<web-resource-collection>
<url-pattern>*.html</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint><role-name>admin</role-name></auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<url-pattern>*.html</url-pattern>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint><role-name>admin</role-name></auth-constraint>
</security-constraint>
Pattern A is immune — no method list means all methods are constrained.
Pattern B is also immune — separate constraints are evaluated independently, so the pos scoping bug does not apply.
Only the split-collection pattern inside a single constraint, on an extension pattern, is affected.
Conclusion
CVE-2026-43515 is a genuine bug and a genuine bypass. The exploit is reproducible, the mechanism is clear, and the fix is correct.
However, the CRITICAL severity rating warrants scrutiny. The vulnerability requires a web.xml configuration that is technically valid but uncommon in production: per-method access control split across multiple <web-resource-collection> blocks inside the same <security-constraint>, on an extension pattern. Deployments that omit <http-method> entirely — the majority — are not affected.
The bypass is confirmed. The real-world impact is context-dependent.
Audit your web.xml. If you use per-method constraints with extension patterns in a split-collection layout, you are exposed. Patch to 7.0.109, 8.5.101, 9.0.118, 10.1.55, or 11.0.22.
References
| Resource | Link |
|---|---|
| Fix commit — 11.0.x | apache/tomcat@276087d |
| PoC repository | covepseng/cve-2026-43515-poc |