HexBrain

curl · Report #3627638

Security Vulnerability Report: Protocol Injection via Programmatic Options

N/Astatus
14upvotes
26 Mar 2026disclosed

How it was found

Summary

Multiple text-based protocol handlers in libcurl (including FTP, SMTP, POP3, and IMAP) are vulnerable to protocol command injection. This occurs when an application sets credentials or other protocol-specific options programmatically (e.g., via CURLOPT_USERNAME, CURLOPT_PASSWORD, or CURLOPT_MAIL_FROM) using strings that contain CRLF (\r\n) sequences. These unsanitized inputs are passed directly to the protocol state machines and sent over the wire, allowing an attacker to inject arbitrary protocol commands.

Affected Protocols

  • FTP: Injection via USER and PASS commands.
  • SMTP: Injection via MAIL FROM, RCPT TO, and SASL authentication tokens.
  • POP3: Injection via USER and PASS commands.
  • IMAP: Injection via LOGIN command (improper quoting of CRLF).

Root Cause Analysis

The vulnerability stems from a lack of centralized sanitization for programmatic string options in lib/setopt.c and a corresponding lack of final-stage sanitization in the low-level Curl_pp_sendf function in lib/pingpong.c.

While curl implements robust sanitization for credentials provided via URLs (using REJECT_CTRL in Curl_urldecode) and .netrc files (using str_has_ctrl in override_login), these checks are bypassed when options are set programmatically.

Vulnerable Code Path (Example: FTP)

  1. Input: Application calls curl_easy_setopt(curl, CURLOPT_USERNAME, "user\r\nINJECTED_COMMAND").
  2. Storage: lib/setopt.c stores the string without validation.
  3. Transfer: lib/transfer.c copies the string to the internal connection structure.
  4. Protocol Execution: lib/ftp.c calls ftp_state_user():
    /* lib/ftp.c:3547 */
    result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", ftp->user);
    
  5. Transmission: lib/pingpong.c's Curl_pp_sendf appends a final \r\n and sends the data:
    USER user
    INJECTED_COMMAND
    \r\n
    

Proof of Concept

A simple C program demonstrates the injection by setting a malicious username:

#include <curl/curl.h>

int main(void) {
  CURL *curl = curl_easy_init();
  if(curl) {
    /* Injecting a second USER command or any other protocol command */
    curl_easy_setopt(curl, CURLOPT_URL, "ftp://127.0.0.1:2121/");
    curl_easy_setopt(curl, CURLOPT_USERNAME, "user\r\nINJECTED_COMMAND");
    curl_easy_perform(curl);
    curl_easy_cleanup(curl);
  }
  return 0;
}

Remediation

  1. Centralized Sanitization: Implement a str_has_ctrl check in lib/setopt.c for all string options that are used in text-based protocol commands, unless the protocol explicitly allows control characters (marked by PROTOPT_USERPWDCTRL).
  2. Protocol-Level Defense: Enhance Curl_pp_sendf to detect and reject (or safely escape) internal CRLF sequences in formatted commands.
  3. Consistent Validation: Ensure that CURLOPT_USERNAME, CURLOPT_PASSWORD, and protocol-specific options like CURLOPT_MAIL_FROM are validated against the same standards as URL-based credentials.

Impact

An attacker who can influence the strings passed to curl_easy_setopt in a libcurl-based application can:

  • Bypass authentication by injecting subsequent commands.
  • Perform unauthorized actions (e.g., deleting files via FTP, sending spoofed emails via SMTP).
  • Exfiltrate data by redirecting protocol flows.

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