Skip to content

Content-Length with whitespace before a list comma (5 ,5) is accepted, then framed as no body #60

Open

Description

@MDA2AV

What happens

UltraHardenedParser accepts a Content-Length list with whitespace before a comma. BodyFramingDetector then frames that request as having no body:

Content-Length: accepted DetectBodyFraming
5 yes ContentLength 5
5,5 yes ContentLength 5
5, 5 yes ContentLength 5
5 ,5 yes None
5\t, 5 yes None
5 , 5 yes None

The two halves disagree about the whitespace:

  • The validator allows OWS on both sides of each element. SemIsValidContentLengthValue (UltraHardenedParser.FullHeader.ROM.cs:371) skips OWS after each number, before the comma (:418).
  • SemHasConflictingCommaSeparatedCL trims that OWS before comparing the elements.
  • The framing parse keeps it:
// BodyFramingDetector.cs, ParseContentLengthDigits
int comma = value.IndexOf((byte)',');
if (comma >= 0)
    value = value[..comma];          // :150 - "5 ": the space before the comma stays
...
if (b < (byte)'0' || b > (byte)'9') return -1;   // :158

-1 is not > 0 (:52), so DetectBodyFraming returns NoBody (:56).

Why it matters

The body bytes are read as the start of the next request on the connection. A front end that frames this header as 5 bytes, as RFC 9110 §8.6 intends, and a back end on Glyph11 then disagree about where the request ends. That is request smuggling, from a header the hardened parser has validated.

Responses go through the same parse: DetectResponseBodyFraming returns UntilClose for Content-Length: 5 ,5 (:131). A client then reads every later byte on the connection as this response's body.

Fix

Trim OWS from the first element before parsing it. Better, parse it with the validator's own loop, so the two can no longer disagree. Ktron's Kotlin port of Glyph11 fixes this in MDA2AV/Ktron@c6504d6.

main (f5d2cc1) and the NuGet package 0.3.7 both give the table above. These vectors are in the request corpus of Ktron's port, which found them.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions