ftp: reject PWD responses containing control characters#20949
ftp: reject PWD responses containing control characters#20949flightlesstux wants to merge 10 commits intocurl:masterfrom
Conversation
A malicious or compromised FTP server could include control characters (e.g. bare \r, or bytes 0x01-0x1f/0x7f) inside the quoted directory path of its 257 PWD response. That string is stored verbatim as ftpc->entrypath and later sent unescaped in a CWD command on connection reuse via Curl_pp_sendf(), which performs no sanitization before appending \r\n. Reject the entire path if any control character is encountered during extraction so that tainted data never reaches a subsequent FTP command.
|
It would also be cool to have a test case verifying this. Something like this: |
Verify that curl rejects a 257 PWD response that contains a control character inside the quoted path, returning CURLE_WEIRD_SERVER_REPLY.
|
Thanks for the suggestion! I've added test3217 that uses the hex encoding to inject a control character (0x03) into the PWD response and verifies curl bails out with error code 8. Pushed it in the second commit. Let me know if the test format needs any tweaking or if you'd like a different test number range. |
|
You did not incorporate my other proposals though, one of which causes the build error. Which indicates you did not compile this locally? |
Instead of using a server-supplied entry path that contains control characters (bytes < 0x20 or 0x7f), discard it silently and continue. On connection reuse curl sends 'CWD <entrypath>' to return to the starting directory. Accepting control characters in that path would let a malicious server inject bytes into a subsequent FTP command. Adjust test3217 accordingly: the transfer still succeeds, curl just has no stored entry path for that connection.
Use curl's own ISCNTRL() macro from curl_ctype.h instead of an open coded check, and return CURLE_WEIRD_SERVER_REPLY when control characters are found in the server-supplied PWD path. Update test3217 to expect error code 8 and the truncated protocol exchange that results from rejecting the bad path.
|
Thanks for catching that. I've applied both inline suggestions - switched to ISCNTRL() with the curl_ctype.h include, and changed it back to returning CURLE_WEIRD_SERVER_REPLY with proper cleanup before the return. Updated the test to expect error code 8 and the shortened protocol exchange. Should be good now. |
\r and \n signal end-of-line in unclosed-quote PWD responses and must only stop path extraction (break), not abort the connection. Other control characters (SOH, STX, BEL, etc.) have no legitimate place in a file path and trigger CURLE_WEIRD_SERVER_REPLY. Also add test3217 to tests/data/Makefile.am so the test runner picks it up without a warning.
Remove the special leniency for \r and \n inside quoted PWD paths. All control characters now uniformly return CURLE_WEIRD_SERVER_REPLY. Add test3218 to verify CR in PWD path is rejected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a server sends a PWD response with an unclosed quote followed by the normal CRLF line terminator (e.g. 257 "just one\r\n), the \r was being treated as an injected control character and caused CURLE_WEIRD_SERVER_REPLY. This broke test 1152. Distinguish between a bare CR/LF that ends the response line (unclosed quote case -- just break and ignore the path) and a CR or other control character embedded inside a properly-quoted path (injected -- error). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the special leniency for CR/LF in PWD path parsing. Any control character inside a quoted PWD path, including CR and LF, is treated as a protocol error and returns CURLE_WEIRD_SERVER_REPLY. Update test1152 (unclosed quote in PWD response) to expect error code 8 since the CR line terminator inside the unclosed quote now triggers the same control-character error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the <data> block and update keywords since curl now errors out at the PWD stage (CURLE_WEIRD_SERVER_REPLY) before any data transfer occurs when the server sends an unclosed quote in the PWD response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When curlx_dyn_addn fails inside the PWD path parsing loop, the dynbuf must be freed before returning. The missing curlx_dyn_free caused a memory leak detected by the debug build leak tracker, which aborted the process in torture tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
augment review |
🤖 Augment PR SummarySummary: This PR hardens FTP handling by rejecting malicious/invalid PWD (257) responses that contain ASCII control characters, preventing them from being persisted and later reused in commands. Changes:
Technical Notes: The new guard prevents control characters (e.g., CR/LF) from reaching later 🤖 Was this summary useful? React with 👍 or 👎 |
|
Thanks! |
What
The
ftp_pwd_resp()function inlib/ftp.cparses the quoted directory name out of a server's257PWD response and stores it asftpc->entrypath. On connection reuse that string is passed directly toCurl_pp_sendf()as:Curl_pp_vsendf()performs no sanitization - it formats the string, appends\r\n, and sends it. Because the extraction loop accepted any byte except\0and", a server could embed bare\ror other control characters inside the quoted path. Those bytes survive into the CWD command sent on the next connection reuse.Example malicious 257 response:
Some FTP server implementations treat a bare
\ras a command separator, which turns this into a second injected command.Fix
Reject the entire path during extraction if any byte with value
< 0x20or== 0x7fis encountered. This is consistent with howlib/cookie.chandles control bytes in cookie values.Impact
Requires connecting to a malicious or MITM'd FTP server. The injected command is limited to what the server's own credentials allow. Severity is moderate but the fix is a one-liner guard with no effect on legitimate servers.