GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

PYSEC-2026-3849

Vulnerability from pysec - Published: 2026-09-10 09:45 - Updated: 2026-09-10 11:02
VLAI
Details

Summary

HTTPX2 can automatically add a Content-Length header to a request that already contains a caller-supplied Transfer-Encoding header. The resulting HTTP/1.1 request contains both framing headers, which can create an ambiguous message boundary and enable request smuggling or connection desynchronization when processed by intermediaries that disagree about which header takes precedence.

Details

When a request body has a known size, HTTPX2's content encoder returns a default Content-Length. Request._prepare() applies each default header with setdefault(), which only checks whether that same header is already present. It does not check whether the mutually exclusive Transfer-Encoding header is present.

For example:

import httpx2

request = httpx2.Request(
    "POST",
    "http://example.com/",
    headers={"Transfer-Encoding": "chunked"},
    content=b"test 123",
)

print(request.headers)

The request contains both:

Transfer-Encoding: chunked
Content-Length: 8

On an HTTP/1.1 connection, the body is serialized using chunked transfer coding while both headers are sent on the wire. This violates HTTP message-framing requirements. Fixed-size byte, JSON, form, and known-length multipart bodies can reach the affected path.

Streaming bodies with an explicit Content-Length are not affected in current HTTPX2 releases because the automatically generated Transfer-Encoding is already suppressed in that direction.

Impact

An attacker may be able to use the conflicting framing headers as a request-smuggling or desynchronization primitive. Exploitation requires an application to pass attacker-controlled request framing headers and associated body data to HTTPX2, use HTTP/1.1, and communicate through a proxy or origin that accepts conflicting headers and interprets them differently from another hop.

Depending on the downstream infrastructure, successful exploitation could interfere with requests sharing a persistent connection, bypass front-end routing or authorization decisions, or poison responses or caches. Applications that do not forward attacker-controlled Transfer-Encoding headers are not directly exposed.

Mitigation

Upgrade to HTTPX2 2.11.0 or later. Patched versions treat Content-Length and Transfer-Encoding as mutually exclusive when applying automatically generated request headers.

If upgrading is not immediately possible, remove Transfer-Encoding and other hop-by-hop framing headers from untrusted input before constructing outbound requests. Applications acting as proxies should derive outbound framing from the body rather than forwarding inbound Content-Length or Transfer-Encoding headers.

Impacted products
Name purl
httpx2 pkg:pypi/httpx2

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "httpx2",
        "purl": "pkg:pypi/httpx2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.11.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ],
      "versions": [
        "0.0.0",
        "2.0.0",
        "2.0.0b1",
        "2.1.0",
        "2.10.0",
        "2.2.0",
        "2.3.0",
        "2.4.0",
        "2.5.0",
        "2.6.0",
        "2.7.0",
        "2.8.0",
        "2.9.0",
        "2.9.1"
      ]
    }
  ],
  "aliases": [
    "CVE-2026-84380",
    "GHSA-pf96-p4fj-6566"
  ],
  "details": "### Summary\n\nHTTPX2 can automatically add a `Content-Length` header to a request that already contains a caller-supplied `Transfer-Encoding` header. The resulting HTTP/1.1 request contains both framing headers, which can create an ambiguous message boundary and enable request smuggling or connection desynchronization when processed by intermediaries that disagree about which header takes precedence.\n\n### Details\n\nWhen a request body has a known size, HTTPX2\u0027s content encoder returns a default `Content-Length`. `Request._prepare()` applies each default header with `setdefault()`, which only checks whether that same header is already present. It does not check whether the mutually exclusive `Transfer-Encoding` header is present.\n\nFor example:\n\n```python\nimport httpx2\n\nrequest = httpx2.Request(\n    \"POST\",\n    \"http://example.com/\",\n    headers={\"Transfer-Encoding\": \"chunked\"},\n    content=b\"test 123\",\n)\n\nprint(request.headers)\n```\n\nThe request contains both:\n\n```text\nTransfer-Encoding: chunked\nContent-Length: 8\n```\n\nOn an HTTP/1.1 connection, the body is serialized using chunked transfer coding while both headers are sent on the wire. This violates HTTP message-framing requirements. Fixed-size byte, JSON, form, and known-length multipart bodies can reach the affected path.\n\nStreaming bodies with an explicit `Content-Length` are not affected in current HTTPX2 releases because the automatically generated `Transfer-Encoding` is already suppressed in that direction.\n\n### Impact\n\nAn attacker may be able to use the conflicting framing headers as a request-smuggling or desynchronization primitive. Exploitation requires an application to pass attacker-controlled request framing headers and associated body data to HTTPX2, use HTTP/1.1, and communicate through a proxy or origin that accepts conflicting headers and interprets them differently from another hop.\n\nDepending on the downstream infrastructure, successful exploitation could interfere with requests sharing a persistent connection, bypass front-end routing or authorization decisions, or poison responses or caches. Applications that do not forward attacker-controlled `Transfer-Encoding` headers are not directly exposed.\n\n### Mitigation\n\nUpgrade to HTTPX2 `2.11.0` or later. Patched versions treat `Content-Length` and `Transfer-Encoding` as mutually exclusive when applying automatically generated request headers.\n\nIf upgrading is not immediately possible, remove `Transfer-Encoding` and other hop-by-hop framing headers from untrusted input before constructing outbound requests. Applications acting as proxies should derive outbound framing from the body rather than forwarding inbound `Content-Length` or `Transfer-Encoding` headers.",
  "id": "PYSEC-2026-3849",
  "modified": "2026-09-10T11:02:09.530035Z",
  "published": "2026-09-10T09:45:01.065667Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/security/advisories/GHSA-pf96-p4fj-6566"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-84380"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/pull/1137"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/commit/829b93a2393212996f613e635261f777d9ec6eab"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/pydantic/httpx2"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pydantic/httpx2/releases/tag/v2.11.0"
    },
    {
      "type": "PACKAGE",
      "url": "https://pypi.org/project/httpx2"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-pf96-p4fj-6566"
    }
  ],
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "HTTPX2: Conflicting Content-Length and Transfer-Encoding headers can be auto-generated"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…