#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "curl-cffi>=0.15.0",
# ]
# ///
from toadex.bug_bank_detector import Target, run

DETECTOR_ID = "cve-2026-24880-tomcat-invalid-chunk-extension"
DETECTOR_TITLE = "Apache Tomcat CVE-2026-24880 invalid chunk-extension request smuggling"
DETECTOR_SEVERITY = "low"
DETECTOR_DESCRIPTION = """Apache Tomcat did not validate HTTP/1.1 chunk-extension grammar before 11.0.19, 10.1.53 and 9.0.115 (CVE-2026-24880). A reverse proxy that permits CRLF inside a quoted chunk extension while Tomcat terminates the extension at the first CRLF can create a request-boundary disagreement and request smuggling.

This detector is fingerprint-only because ordinary HTTP clients cannot send the malformed framing and parser acceptance alone does not establish exploitable desynchronization. Manually send a final chunk shaped like `0;x=\"y\\r\\n\\r\\nGET /marker HTTP/1.1...X-Absorb: \"\\r\\n\\r\\n`. After reading the outer response, wait before sending another request. A second response already delivered on the same client connection, or behavior identical to a correctly closed chunk extension followed by normal pipelining, does not prove response-queue poisoning. Report only when a controlled follow-up or separate controlled client receives a uniquely marked backend response that was withheld or misassociated, or when the embedded request bypasses a concrete proxy security control.

Primary references: https://tomcat.apache.org/security-11.html, https://tomcat.apache.org/security-10.html, https://tomcat.apache.org/security-9.html, https://github.com/apache/tomcat/commit/f07df938d00f7419b40fa65aa912966d0efac522 and https://github.com/apache/tomcat/commit/1e71441a15972f56e661b0b549fb9e5d838b83bb."""


async def fingerprint(target: Target) -> bool:
    """Identify Tomcat from application stack frames rather than a generic Server header."""
    response = await target.get("/")
    body = response.text
    return (
        "org.apache.catalina." in body
        and "org.apache.coyote." in body
        and "org.apache.tomcat." in body
    )


if __name__ == "__main__":
    raise SystemExit(run(fingerprint=fingerprint))
