Lucene search

K
hackeroneKurohiroH1:1954658
HistoryApr 19, 2023 - 1:43 p.m.

curl: CVE-2023-28322: more POST-after-PUT confusion

2023-04-1913:43:09
kurohiro
hackerone.com
128
cve-2023-28322
post data injection
data exposure
libcurl bug

0.007 Low

EPSS

Percentile

80.3%

Summary:

CVE-2022-32221 fixes is insufficient.
In CVE-2022-32221, only CURLOPT_POST was corrected.
However, CURLOPT_POST is not necessarily used when sending data with the POST method.
CURLOPT_POST is not used in the CURLOPT_POSTFIELDS usage example on the official website.

CURL *curl = curl_easy_init();
if(curl) {
  const char *data = "data to send";
 
  curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
 
  /* size of the POST data */
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 12L);
 
  /* pass in a pointer to the data - libcurl will not copy */
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
 
  curl_easy_perform(curl);
}

Also on this page is the following statement.

>Using CURLOPT_POSTFIELDS implies setting CURLOPT_POST to 1.

https://curl.se/libcurl/c/CURLOPT_POSTFIELDS.html

I think it means that some users do not use CURLOPT_POST.
Just to be clear, CURLOPT_POSTFIELDS does not set a FLASE on data->set.upload.

CURLOPT_POST is not used in the CURLOPT_MIMEPOST usage example either.
https://curl.se/libcurl/c/CURLOPT_MIMEPOST.html

Based on the above, I think we need to modify the following to assign FALSE to data->set.upload if we use the following.

  • CURLOPT_POSTFIELDS
  • CURLOPT_COPYPOSTFIELDS
  • CURLOPT_MIMEPOST

We could not determine the deprecated CURLOPT_HTTPPOST.

Steps To Reproduce:

Almost the same source as #1704017. The difference is that line 52 is commented out.

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

typedef struct
{
    char *buf;
    size_t len;
} put_buffer;

static size_t put_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
    put_buffer *putdata = (put_buffer *)stream;
    size_t totalsize = size * nmemb;
    size_t tocopy = (putdata->len < totalsize) ? putdata->len : totalsize;
    memcpy(ptr, putdata->buf, tocopy);
    putdata->len -= tocopy;
    putdata->buf += tocopy;
    return tocopy;
}

int main()
{
    CURL *curl = NULL;
    put_buffer pbuf = {};
    char *otherdata = "This is some other data";

    curl_global_init(CURL_GLOBAL_DEFAULT);

    curl = curl_easy_init();

    // PUT
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, put_callback);
    pbuf.buf = strdup("This is highly secret and sensitive data");
    pbuf.len = strlen(pbuf.buf);
    curl_easy_setopt(curl, CURLOPT_READDATA, &pbuf);
    curl_easy_setopt(curl, CURLOPT_INFILESIZE, pbuf.len);
    curl_easy_setopt(curl, CURLOPT_URL, "http://host1.com/putsecretdata");
    curl_easy_perform(curl);

    // Without this line, a PUT instead of a POST will be sent below (this is a bug in libcurl)
    //curl_easy_setopt(curl, CURLOPT_UPLOAD, 0L);

    // Without this line, the POST below will send "This is highly secret and sensitive data"
    //    when instead the user intended to send "This is some other data"
    // With this line, the program will attempt to use freed data, causing a segfault or any number
    //    of potential exploits.
    //free(pbuf.buf);

    // POST (will be a PUT without the line just above)
    //curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, otherdata);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(otherdata));
    curl_easy_setopt(curl, CURLOPT_URL, "http://host2.com/postotherdata");
    curl_easy_perform(curl);

    curl_easy_cleanup(curl);

    curl_global_cleanup();

    return 0;
}

Supporting Material/References:

[list any additional material (e.g. screenshots, logs, etc.)]

  • [attachment / reference]

Impact

An attacker could potentially inject data, either from stdin or from an unintended buffer. Further, without even an active attacker, this could lead to segfaults or sensitive information being exposed to an unintended recipient.