Subject: #879: Should servers interpret Transfer-Encoding in 1.0 requests?

From: Mark Nottingham <[email-masked]>
Date: Tue, 13 Jul 2021 17:43:28 +1000

<https://github.com/httpwg/http-core/issues/879>

Some security researchers have found what appears to be a situation where handling of Transfer-Encoding and Content-Length in a particular deployment can introduce a request smuggling vulnerability, even if the specification's requirements are followed closely. 

See the issue for details. The heart of the question at this point is whether we can strengthen (to a SHOULD or MUST) or otherwise qualify this 'ought':

> If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length.  Such a message might indicate an attempt to perform request smuggling (Section 9.5) or response splitting (Section 9.4) and ought to be handled as an error.

Their research indicates that a number of servers don't reject such requests.

Could implementers take a look and weigh in (here or on the issue)?

Cheers,

--
Mark Nottingham   https://www.mnot.net/

---

From: Amos Jeffries <[email-masked]>
Date: Sun, 18 Jul 2021 19:49:43 +1200

> Could implementers take a look and weigh in (here or on the issue)?

There is allowance in the main spec for senders to deliver HTTP/1.0 on 
messages even if the sender supports a higher version if it believes the 
server is HTTP/1.0-only.

Squid has configurable strict and relaxed modes of message parse/validate.

The relaxed mode (default) accepts such messages as if HTTP/1.1 sender 
was trying to work around such buggy servers. Obeys the spec rules about 
T-E overriding C-L. Then goes right on and sends HTTP/1.1 version to the 
server anyway.
  *IF* the client was right about the servers bugs needing "HTTP/1.0" we 
are probably wrong to send the "HTTP/1.1". But I am not aware of any 
problems being reported about it, ever.

The strict mode should be responding with an 'invalid-request' error 
about framing issues spotted.

Amos

---

From: Greg Wilkins <[email-masked]>
Date: Mon, 19 Jul 2021 13:01:41 +1000

Jetty server is similar to squid.  By default we will send a 400 response
to any request with both a Transfer-Encoding and a Content-Length.
However, we do have a legacy compliance mode that will allow both, in which
case if the Transfer-Encoding includes chunking, then it is used rather
than the content-length.

If you have some specific exploits that are of concern with such a mode,
then please send them to us on [email-masked] and we'd like to try
them in this mode to see if any of our other checks will catch anything
strange about them.

cheers

On Sun, 18 Jul 2021 at 17:54, Amos Jeffries <[email-masked]> wrote:

> > Some security researchers have found what appears to be a situation
> where handling of Transfer-Encoding and Content-Length in a particular
> deployment can introduce a request smuggling vulnerability, even if the
> specification's requirements are followed closely.
> >
> > See the issue for details. The heart of the question at this point is
> whether we can strengthen (to a SHOULD or MUST) or otherwise qualify this
> 'ought':
> >
> >> If a message is received with both a Transfer-Encoding and a
> Content-Length header field, the Transfer-Encoding overrides the
> Content-Length.  Such a message might indicate an attempt to perform
> request smuggling (Section 9.5) or response splitting (Section 9.4) and
> ought to be handled as an error.
> >

-- 
Greg Wilkins <[email-masked]> CTO http://webtide.com

---

From: Mark Nottingham <[email-masked]>
Date: Mon, 19 Jul 2021 15:48:45 +1000

So it sounds like 'MUST be handled as an error' might work (perhaps with some additional details about why it's unsafe): a specific compatibility mode that doesn't claim conformance is out of scope. We might want to add something like:

> Note that some implementations can be configured to process messages with both Content-Length and Transfer-Encoding to improve compatibility with non-conformant peers. Such modes should be only used when the security and interoperability considerations (as per above) are understood.

?

> On 19 Jul 2021, at 1:01 pm, Greg Wilkins <[email-masked]> wrote:
> 
> 
> Jetty server is similar to squid.  By default we will send a 400 response to any request with both a Transfer-Encoding and a Content-Length.  However, we do have a legacy compliance mode that will allow both, in which case if the Transfer-Encoding includes chunking, then it is used rather than the content-length.
> 
> If you have some specific exploits that are of concern with such a mode, then please send them to us on [email-masked] and we'd like to try them in this mode to see if any of our other checks will catch anything strange about them.
> 
> cheers
> 
> 
> 
> 
>

---

From: Willy Tarreau <[email-masked]>
Date: Mon, 19 Jul 2021 10:49:32 +0200

On Mon, Jul 19, 2021 at 03:48:45PM +1000, Mark Nottingham wrote:
> So it sounds like 'MUST be handled as an error' might work (perhaps with some
> additional details about why it's unsafe): a specific compatibility mode that
> doesn't claim conformance is out of scope. We might want to add something
> like:
> 
> > Note that some implementations can be configured to process messages with
> > both Content-Length and Transfer-Encoding to improve compatibility with
> > non-conformant peers. Such modes should be only used when the security and
> > interoperability considerations (as per above) are understood.

After giving it more thoughts, I'm not sure this is either necessary nor
sufficient. The problem mostly is about 1.0 intermediaries forwarding T-E
delimited messages to 1.1-capable servers without understanding T-E
boundaries. By definition we cannot act on those outdated intermediaries
and we cannot completely rule out the risk that some of them would simply
skip a chunk-size as an invalid method (e.g. "0<CR><LF>") or consume the
request body as defined by the C-L header and strip it before passing
the request, or even confuse a trailing chunk size with a valid request.
In such a case, a second request made of a method consisting only in hex
digits could be accepted by the intermediary, and be used as a chunk size
by the net hop or final server (especially if the URI is made of a ';'
which can be taken for a chunk-extension).

E.g.

     GET /req1 HTTP/1.0\r\n
     Transfer-encoding: chunked\r\n
     Connection: keep-alive\r\n

     A0 ;/req2 HTTP/1.0\r\n
     Connection: keep-alive\r\n
     X-folded-header: ~130 bytes of garbage\r\n
      0\r\n

     GET /req3 HTTP/1.0
     Connection: keep-alive

A lenient 1.0 proxy could take "A0" for a valid method, and ";/req2"
as a valid URI, and pass this on. The 1.1 server waiting for a chunk
size would read A0 as a 160 bytes chunk, followed by a chunk extension,
then some contents, till the " 0\r\n" that completes a folding header
field for the proxy and that could validate the final chunk for the
1.1 server. Then we're desynchronized.

For this to happen it would require that the proxy supports pipelining,
or that it's very lenient (like those which used to work on packets).
But again we're speaking about an uncontrolled, outdated 1.0 proxy.

As such, I'm wondering now if the safest solution that doesn't risk
breaking tons of stuff wouldn't simply be to state that an 1.0
request that carries a Transfer-Encoding header field must always be
considered as the last one on that connection and that the server must
close after responding to it. In the worst case the server will parse
more data than expected for what it will take for a single request
while the proxy would take it for two or more, but this will prevent
such a connection from being desynchronized, cached from being abused
or filtering from being bypassed. And it will implicitly address the
case of C-L + T-E in 1.0 through these.

Note, we could also state that C-L + T-E indicates that the sender is
not fully 1.1-compliant, thus probably at risk, and apply the same
fall back there.

It would then result in something like this:

  - a request received with both Content-Length and Transfer-Encoding
    MAY be rejected as invalid, and in any case the connection MUST
    always be closed after processing that request. Such a request
    indicates that the sender does not fully respect the standard
    and might be at risk of conveying a request smuggling attack ;

  - an HTTP/1.0 request received with a Transfer-Encoding header field
    MAY be rejected as invalid, and in any case the connection MUST
    always be closed after processing that request. Its sender might
    not be aware of the chunked transfer coding and might be at risk
    of conveying a request smuggling attack.

I don't think it would be too difficult to enforce for most
implementations and shouldn't visibly affect any legitimate traffic.

Thoughts ?

Willy

---

From: Mark Nottingham <[email-masked]>
Date: Fri, 23 Jul 2021 11:56:51 +1000

We seem to have converged on a resolution here:
  https://github.com/httpwg/http-core/pull/905

Please take a look and if you have questions, comments or objections, let us know.

Cheers,

> On 13 Jul 2021, at 5:43 pm, Mark Nottingham <[email-masked]> wrote:
>

---

From: Willy Tarreau <[email-masked]>
Date: Fri, 23 Jul 2021 07:19:24 +0200

> Please take a look and if you have questions, comments or objections, let us

Adding that to haproxy's short-term TODO list :-)

Cheers,
Willy

---

From: Stefan Eissing <[email-masked]>
Date: Fri, 23 Jul 2021 08:46:46 +0200

> Am 23.07.2021 um 07:19 schrieb Willy Tarreau <[email-masked]>:
> 

At first glance read that you have a short TODO list and became envious... X-)