curl · Report #3627638
Security Vulnerability Report: Protocol Injection via Programmatic Options
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
USERandPASScommands. - SMTP: Injection via
MAIL FROM,RCPT TO, and SASL authentication tokens. - POP3: Injection via
USERandPASScommands. - IMAP: Injection via
LOGINcommand (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)
- Input: Application calls curl_easy_setopt(curl, CURLOPT_USERNAME, "user\r\nINJECTED_COMMAND").
- Storage: lib/setopt.c stores the string without validation.
- Transfer: lib/transfer.c copies the string to the internal connection structure.
- Protocol Execution: lib/ftp.c calls ftp_state_user():
/* lib/ftp.c:3547 */ result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", ftp->user); - Transmission: lib/pingpong.c's Curl_pp_sendf appends a final
\r\nand 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
- 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). - Protocol-Level Defense: Enhance Curl_pp_sendf to detect and reject (or safely escape) internal CRLF sequences in formatted commands.
- Consistent Validation: Ensure that
CURLOPT_USERNAME,CURLOPT_PASSWORD, and protocol-specific options likeCURLOPT_MAIL_FROMare 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.