Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Header-name length mismatch #19227

@MegaManSec

Description

@MegaManSec

I did this

In lib/http_proxy.c, an incorrect comparison makes a header comparison fail.

This is the description from ZeroPath:

dynhds_add_custom parses header names by locating ':' and computing namelen as the number of characters before the colon (namelen excludes the colon). Later the code checks for Authorization: and Cookie: using hd_name_eq(name, namelen, STRCONST("Authorization:")) / STRCONST("Cookie:"). hd_name_eq first requires the two lengths to be equal before performing a case-insensitive compare. Because the constant includes the colon in its length but namelen excludes it, the length comparison fails and the intended suppression branch is skipped.

It can be confirmed with the following patch:

diff --git a/lib/http_proxy.c b/lib/http_proxy.c
index 845ba2e8f..5ccd656bf 100644
--- a/lib/http_proxy.c
+++ b/lib/http_proxy.c
@@ -54,6 +54,19 @@ static bool hd_name_eq(const char *n1, size_t n1len,
   return (n1len == n2len) ? curl_strnequal(n1, n2, n1len) : FALSE;
 }
 
+/* DEBUG helper: show what we are comparing.
+   Only useful with -v since infof prints in verbose mode. */
+static void debug_hdrcmp(struct Curl_easy *data,
+                         const char *name, size_t namelen,
+                         const char *label,
+                         const char *cstr, size_t clen)
+{
+  infof(data,
+        "DEBUG hdrcmp %-18s name='%.*s' len=%zu  const='%s' len=%zu  => %s",
+        label, (int)namelen, name, namelen, cstr, clen,
+        hd_name_eq(name, namelen, cstr, clen) ? "MATCH" : "NO MATCH");
+}
+
 static CURLcode dynhds_add_custom(struct Curl_easy *data,
                                   bool is_connect, int httpversion,
                                   struct dynhds *hds)
@@ -143,6 +156,21 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
       }
 
       DEBUGASSERT(name && value);
+      /* Show comparisons so we can see why branches are or are not taken. */
+      if(data->set.verbose) {
+        debug_hdrcmp(data, name, namelen, "Host:",
+                     STRCONST("Host:"));
+        debug_hdrcmp(data, name, namelen, "Content-Type:",
+                     STRCONST("Content-Type:"));
+        debug_hdrcmp(data, name, namelen, "Content-Length:",
+                     STRCONST("Content-Length:"));
+        debug_hdrcmp(data, name, namelen, "Transfer-Encoding:",
+                     STRCONST("Transfer-Encoding:"));
+        debug_hdrcmp(data, name, namelen, "Authorization:",
+                     STRCONST("Authorization:"));
+        debug_hdrcmp(data, name, namelen, "Cookie:",
+                     STRCONST("Cookie:"));
+      }
       if(data->state.aptr.host &&
          /* a Host: header was sent already, do not pass on any custom Host:
             header as that will produce *two* in the same request! */

...

$ ./src/curl -vkL --http1.1   --proxy http://127.0.0.1:8080   https://a.test:4443/   --resolve a.test:4443:127.0.0.1   --resolve b.test:4444:127.0.0.1   --cacert cert.pem   --proxy-header 'Authorization: Bearer LEAK'   --proxy-header 'Cookie: a=1'
* Added a.test:4443:127.0.0.1 to DNS cache
* Added b.test:4444:127.0.0.1 to DNS cache
*   Trying 127.0.0.1:8080...
* CONNECT: no ALPN negotiated
* allocate connect buffer
* DEBUG hdrcmp Host:              name='Authorization' len=13  const='Host:' len=5  => NO MATCH
* DEBUG hdrcmp Content-Type:      name='Authorization' len=13  const='Content-Type:' len=13  => NO MATCH
* DEBUG hdrcmp Content-Length:    name='Authorization' len=13  const='Content-Length:' len=15  => NO MATCH
* DEBUG hdrcmp Transfer-Encoding: name='Authorization' len=13  const='Transfer-Encoding:' len=18  => NO MATCH
* DEBUG hdrcmp Authorization:     name='Authorization' len=13  const='Authorization:' len=14  => NO MATCH
* DEBUG hdrcmp Cookie:            name='Authorization' len=13  const='Cookie:' len=7  => NO MATCH
* DEBUG hdrcmp Host:              name='Cookie' len=6  const='Host:' len=5  => NO MATCH
* DEBUG hdrcmp Content-Type:      name='Cookie' len=6  const='Content-Type:' len=13  => NO MATCH
* DEBUG hdrcmp Content-Length:    name='Cookie' len=6  const='Content-Length:' len=15  => NO MATCH
* DEBUG hdrcmp Transfer-Encoding: name='Cookie' len=6  const='Transfer-Encoding:' len=18  => NO MATCH
* DEBUG hdrcmp Authorization:     name='Cookie' len=6  const='Authorization:' len=14  => NO MATCH
* DEBUG hdrcmp Cookie:            name='Cookie' len=6  const='Cookie:' len=7  => NO MATCH

Note the extra : in the latter part of the lines.

This bug was found with ZeroPath.

I expected the following

No response

curl/libcurl version

most recent

operating system

all

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions