relayd - HTTP Desync Attacks

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

relayd - HTTP Desync Attacks

pdcvgmh
Hi guys! I've been reading about HTTP Desync Attacks lately so I took a
look to relayd's source code to check if it's likely to be exploited.
I wasn't able to do a POC (I don't have a OBSD installation at the moment)
but I think it is.
I'm going to install a OpenBSD as soon as I can in order to test the
current code and send a patch if needed but I want to tell you what I've
seen so far.
There I go:

If a malicious user split a "Transfer-Encoding: chunked" http header in
multiple lines along with a Content-Length header it would trick relayd to
use the content length header while forwarding the
Transfer-Encoding header downstream.
The problem is in relay_read_http:

if (strcasecmp("Transfer-Encoding", key) == 0 &&
strcasecmp("chunked", value) == 0)
desc->http_chunked = 1;

The check for key == "Transfer-Encoding" && value == chunked isn't deferred
until the full header is read and it should.
Maybe we could defer the check until we are out of the loop.

Replacing
if (desc->http_chunked) {
/* Chunked transfer encoding */
cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
bev->readcb = relay_read_httpchunks;
}

by doing a lookup into desc->http_headers

In order to achieve this, the function kv_extend should be modified in
order to alter the structure pointed by the first parameter (today it does
nothing with the first parameter)

Also, It seems like the "lookup" label along with the goto lookup sentence
could be removed.

Payload
POST ...
...
Transfer-Encoding:
 chunked
...
Content-Length: whatever greater than the chunk

10
1234567890123456
POISON!

If I'm right this payload would result in relayd honouring the
Content-Length header (breaking RFC 2616) and probably poisoning the socket
(assuming that the downstream server would honour the Transfer-Encoding
header)

PD: I haven't digged enough to realize if relayd reuse downstream
connections among different clients so I can't say the real impact of this
issue.

Best regards

Pablo
Reply | Threaded
Open this post in threaded view
|

Re: relayd - HTTP Desync Attacks

pdcvgmh
"In order to achieve this, the function kv_extend should be modified in
order to alter..." Wrong! The header (already added to desc->http_headers)
gets modified correctly through the desc->http_lastheader pointer.

I'm going to stop making assumptions... I have to get my hands on a working
OBSD setup and make some real test.

Best regards

On Wed, Aug 14, 2019 at 2:27 AM Pablo Caballero <[hidden email]> wrote:

> Hi guys! I've been reading about HTTP Desync Attacks lately so I took a
> look to relayd's source code to check if it's likely to be exploited.
> I wasn't able to do a POC (I don't have a OBSD installation at the moment)
> but I think it is.
> I'm going to install a OpenBSD as soon as I can in order to test the
> current code and send a patch if needed but I want to tell you what I've
> seen so far.
> There I go:
>
> If a malicious user split a "Transfer-Encoding: chunked" http header in
> multiple lines along with a Content-Length header it would trick relayd to
> use the content length header while forwarding the
> Transfer-Encoding header downstream.
> The problem is in relay_read_http:
>
> if (strcasecmp("Transfer-Encoding", key) == 0 &&
> strcasecmp("chunked", value) == 0)
> desc->http_chunked = 1;
>
> The check for key == "Transfer-Encoding" && value == chunked isn't
> deferred until the full header is read and it should.
> Maybe we could defer the check until we are out of the loop.
>
> Replacing
> if (desc->http_chunked) {
> /* Chunked transfer encoding */
> cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
> bev->readcb = relay_read_httpchunks;
> }
>
> by doing a lookup into desc->http_headers
>
> In order to achieve this, the function kv_extend should be modified in
> order to alter the structure pointed by the first parameter (today it does
> nothing with the first parameter)
>
> Also, It seems like the "lookup" label along with the goto lookup sentence
> could be removed.
>
> Payload
> POST ...
> ...
> Transfer-Encoding:
>  chunked
> ...
> Content-Length: whatever greater than the chunk
>
> 10
> 1234567890123456
> POISON!
>
> If I'm right this payload would result in relayd honouring the
> Content-Length header (breaking RFC 2616) and probably poisoning the socket
> (assuming that the downstream server would honour the Transfer-Encoding
> header)
>
> PD: I haven't digged enough to realize if relayd reuse downstream
> connections among different clients so I can't say the real impact of this
> issue.
>
> Best regards
>
> Pablo
>
Reply | Threaded
Open this post in threaded view
|

Re: relayd - HTTP Desync Attacks

pdcvgmh
Patch inlined

Best regards

Index: usr.sbin/relayd/http.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/http.h,v
retrieving revision 1.11
diff -u -p -u -r1.11 http.h
--- usr.sbin/relayd/http.h 8 May 2019 23:22:19 -0000 1.11
+++ usr.sbin/relayd/http.h 19 Aug 2019 19:35:54 -0000
@@ -239,7 +239,6 @@ struct http_descriptor {

  char *http_host;
  enum httpmethod http_method;
- int http_chunked;
  char *http_version;
  unsigned int http_status;

Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.78
diff -u -p -u -r1.78 relay_http.c
--- usr.sbin/relayd/relay_http.c 13 Jul 2019 06:53:00 -0000 1.78
+++ usr.sbin/relayd/relay_http.c 19 Aug 2019 19:35:54 -0000
@@ -159,7 +159,7 @@ relay_read_http(struct bufferevent *bev,
  int action, unique, ret;
  const char *errstr;
  size_t size, linelen;
- struct kv *hdr = NULL;
+ struct kv *hdr = NULL, lookup;
  struct kv *upgrade = NULL, *upgrade_ws = NULL;

  getmonotime(&con->se_tv_last);
@@ -219,8 +219,7 @@ relay_read_http(struct bufferevent *bev,
  }

  /* Append line to the last header, if present */
- if (kv_extend(&desc->http_headers,
-    desc->http_lastheader, line) == NULL) {
+ if (kv_extend(desc->http_lastheader, line) == NULL) {
  free(line);
  goto fail;
  }
@@ -279,7 +278,6 @@ relay_read_http(struct bufferevent *bev,
  DPRINTF("http_version %s http_rescode %s "
     "http_resmesg %s", desc->http_version,
     desc->http_rescode, desc->http_resmesg);
- goto lookup;
  } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) {
  if ((desc->http_method = relay_httpmethod_byname(key))
     == HTTP_METHOD_NONE) {
@@ -360,11 +358,7 @@ relay_read_http(struct bufferevent *bev,
  goto abort;
  }
  }
- lookup:
- if (strcasecmp("Transfer-Encoding", key) == 0 &&
-    strcasecmp("chunked", value) == 0)
- desc->http_chunked = 1;
-
+
  /* The following header should only occur once */
  if (strcasecmp("Host", key) == 0) {
  unique = 1;
@@ -516,10 +510,16 @@ relay_read_http(struct bufferevent *bev,
  bev->readcb = relay_read_http;
  break;
  }
- if (desc->http_chunked) {
- /* Chunked transfer encoding */
- cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
- bev->readcb = relay_read_httpchunks;
+
+ lookup.kv_key = "Transfer-Encoding";
+ hdr = kv_find(&desc->http_headers, &lookup);
+ if (hdr != NULL && hdr->kv_value != NULL) {
+ value = hdr->kv_value + strspn(hdr->kv_value, " \t\r\n");
+ if (strcasecmp("chunked", value) == 0) {
+ /* Chunked transfer encoding */
+ cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
+ bev->readcb = relay_read_httpchunks;
+ }
  }

  /*
@@ -772,7 +772,6 @@ relay_reset_http(struct ctl_relay_event

  relay_httpdesc_free(desc);
  desc->http_method = 0;
- desc->http_chunked = 0;
  cre->headerlen = 0;
  cre->line = 0;
  cre->done = 0;
Index: usr.sbin/relayd/relayd.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v
retrieving revision 1.180
diff -u -p -u -r1.180 relayd.c
--- usr.sbin/relayd/relayd.c 26 Jun 2019 12:13:47 -0000 1.180
+++ usr.sbin/relayd/relayd.c 19 Aug 2019 19:35:54 -0000
@@ -713,7 +713,7 @@ kv_delete(struct kvtree *keys, struct kv
 }

 struct kv *
-kv_extend(struct kvtree *keys, struct kv *kv, char *value)
+kv_extend(struct kv *kv, char *value)
 {
  char *newvalue;

Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.259
diff -u -p -u -r1.259 relayd.h
--- usr.sbin/relayd/relayd.h 26 Jun 2019 12:13:47 -0000 1.259
+++ usr.sbin/relayd/relayd.h 19 Aug 2019 19:35:55 -0000
@@ -1349,7 +1349,7 @@ struct kv *kv_add(struct kvtree *, char
 int kv_set(struct kv *, char *, ...);
 int kv_setkey(struct kv *, char *, ...);
 void kv_delete(struct kvtree *, struct kv *);
-struct kv *kv_extend(struct kvtree *, struct kv *, char *);
+struct kv *kv_extend(struct kv *, char *);
 void kv_purge(struct kvtree *);
 void kv_free(struct kv *);
 struct kv *kv_inherit(struct kv *, struct kv *);

On Wed, Aug 14, 2019 at 12:40 PM Pablo Caballero <[hidden email]> wrote:

> "In order to achieve this, the function kv_extend should be modified in
> order to alter..." Wrong! The header (already added to desc->http_headers)
> gets modified correctly through the desc->http_lastheader pointer.
>
> I'm going to stop making assumptions... I have to get my hands on a
> working OBSD setup and make some real test.
>
> Best regards
>
> On Wed, Aug 14, 2019 at 2:27 AM Pablo Caballero <[hidden email]> wrote:
>
>> Hi guys! I've been reading about HTTP Desync Attacks lately so I took a
>> look to relayd's source code to check if it's likely to be exploited.
>> I wasn't able to do a POC (I don't have a OBSD installation at the
>> moment) but I think it is.
>> I'm going to install a OpenBSD as soon as I can in order to test the
>> current code and send a patch if needed but I want to tell you what I've
>> seen so far.
>> There I go:
>>
>> If a malicious user split a "Transfer-Encoding: chunked" http header in
>> multiple lines along with a Content-Length header it would trick relayd to
>> use the content length header while forwarding the
>> Transfer-Encoding header downstream.
>> The problem is in relay_read_http:
>>
>> if (strcasecmp("Transfer-Encoding", key) == 0 &&
>> strcasecmp("chunked", value) == 0)
>> desc->http_chunked = 1;
>>
>> The check for key == "Transfer-Encoding" && value == chunked isn't
>> deferred until the full header is read and it should.
>> Maybe we could defer the check until we are out of the loop.
>>
>> Replacing
>> if (desc->http_chunked) {
>> /* Chunked transfer encoding */
>> cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
>> bev->readcb = relay_read_httpchunks;
>> }
>>
>> by doing a lookup into desc->http_headers
>>
>> In order to achieve this, the function kv_extend should be modified in
>> order to alter the structure pointed by the first parameter (today it does
>> nothing with the first parameter)
>>
>> Also, It seems like the "lookup" label along with the goto lookup
>> sentence could be removed.
>>
>> Payload
>> POST ...
>> ...
>> Transfer-Encoding:
>>  chunked
>> ...
>> Content-Length: whatever greater than the chunk
>>
>> 10
>> 1234567890123456
>> POISON!
>>
>> If I'm right this payload would result in relayd honouring the
>> Content-Length header (breaking RFC 2616) and probably poisoning the socket
>> (assuming that the downstream server would honour the Transfer-Encoding
>> header)
>>
>> PD: I haven't digged enough to realize if relayd reuse downstream
>> connections among different clients so I can't say the real impact of this
>> issue.
>>
>> Best regards
>>
>> Pablo
>>
>