HTTP Request Smuggling in pylons/waitress
Mar 10th 2022
Due to several violations of the HTTP standard as defined in RFC7230, Waitress is vulnerable to HTTP request smuggling when used with an upstream proxy that exhibits nonstandard behaviour.
Each issue is explained in the Occurrences section below.
Occurrence 2 - Illegal Characters in Chunked Extensions
When parsing chunked data, chunked extensions are ignored. Ignoring chunked extensions is not a bug in itself. The bug lies in the fact that the parser does not check whether the chunked extension contents conform to the HTTP standard. According to the RFC, only
0x80-0xff are allowed in this area.
Consider the following request.
GET / HTTP/1.1\r\n Transfer-Encoding: chunked\r\n \r\n 2;\nxx\r\n 4c\r\n 0\r\n \r\n GET /admin HTTP/1.1\r\n Transfer-Encoding: chunked\r\n \r\n 0\r\n \r\n
Notice that an illegal LF (
\n) character was added to the chunked extension. Suppose that a frontend proxy uses LF to delimit lines, instead of a CRLF. The frontend would see only one request:
GET / HTTP/1.1 Host: localhost:8080 Transfer-Encoding: chunked 2 xx 4c 0 GET /admin HTTP/1.1 Host: localhost:8080 Transfer-Encoding: chunked 0
However, Waitress sees two requests:
GET / HTTP/1.1 Host: localhost:8080 Transfer-Encoding: chunked 2 4c 0 GET /admin HTTP/1.1 Host: localhost:8080 Transfer-Encoding: chunked 0
This attack was possible in a previous vulnerable version of ATS.
The following command (with Waitress running on port 80) shows that Waitress allows the above PoC and processes both requests.
(printf 'GET / HTTP/1.1\r\n'\ 'Transfer-Encoding: chunked\r\n'\ 'Host: localhost\r\n\r\n'\ "2;\nxx\r\n"\ '47\r\n'\ '0\r\n'\ '\r\n'\ 'GET /forbidden HTTP/1.1\r\n'\ 'Transfer-Encoding: chunked\r\n'\ 'Host: localhost\r\n\r\n'\ '0\r\n'\ '\r\n'; sleep 1) | nc localhost 80
- llhttp (Node.js) CVE-2021-22960 HTTP Request Smuggling when parsing the body
Occurrence 1 - 'Signed' Content Lengths
The content length is not validated for leading
According to RFC7230, the
Content-Length field value must be an integer greater than or equal to zero. Additionally, any sign prefixes are illegal, since only
DIGITs (0-9) are allowed.
Content-Length = 1*DIGIT ... Any Content-Length field value greater than or equal to zero is valid.
Suppose a frontend proxy silently ignores but forwards an invalid
Content-Length header such as
Content-Length: +5 or
Content-Length: -5 to Waitress. The frontend and backend would then disagree on where the request ends. Such behaviour was found on an older version of ATS (9.1.0).
For instance, in the following request, the frontend sees two requests to
/, while Waitress sees a request to
/ and another one to
GET / Content-Length: +23 GET / Dummy: GET /forbidden HTTP/1.1
Occurrence 3 - 'Signed' and 0x-Prefixed Chunk Lengths
Waitress accepts chunk sizes with the
0x prefix. This is because Python's
int() constructor allows
0x-prefixed strings when base-16 is specified. Again, the
- prefixes are also allowed.
For example, the following is a valid request.
GET / Host: example.com Transfer-Encoding: chunked +0x3 abc 0
According to the RFC, chunk sizes should not contain the
0x and sign prefixes.
chunk-size = 1*HEXDIG
This issue may be relevant if an upstream proxy also exhibits vulnerable behaviour when parsing chunk lengths, due to deviations from the standard.
Thanks, @admin. Seems like they can be contacted via email@example.com.
This is Bert from the pylons team. We are reviewing and will create the appropriate fixes as necessary.
This may take a day or two. Thanks for your patience.
@admin I was not aware that you all would assign a CVE, so there may be an overlap here with the one that Github assigns. Let me know how you'd like to handle that.
Thanks a lot!
@maintainer - a CVE will not be assigned here. You are welcome to proceed with
confirming the fix.
If you provide me with the CVE ID, I can assign it to this report 👍
@admin GitHub has issued CVE-2022-24761 for the report. I'm hoping to land the fix within the next day.
CVE added to the report 👍 Once you have committed and pushed your fix, feel free to