HACKERONE

curl: libcurl FTP path normalization flaw allows decoded %2e%2e → CWD .. and directory escape (Path Traversal, CWE-22)_H1:3418861

Description

ftp_parse_url_path in lib/ftp.c URL-decodes FTP path segments (e.g. %2e%2e) and then splits the decoded path into components using an ad-hoc loop that skips empty components produced by //. The code does not perform canonical path normalization (no stack-based handling of . or ..). As a result, encoded traversal sequences such as %2e%2e decode to .. and may become normal path components that cause libcurl to issue CWD .. commands to the remote FTP server. This enables client-driven directory traversal in contexts where an attacker can supply an FTP URL to an application using libcurl.

AI usage statement: I used an AI assistant to help draft and structure this report. All technical claims, tests and trace excerpts were executed and verified manually by me in a local test environment. I did not use AI to generate exploit code.

Affected version

Please include the exact output of curl -V here when submitting.
(Example placeholder — replace with your exact output):

curl 8.4.0 (x86_64-pc-linux-gnu) libcurl/8.4.0 OpenSSL/1.1.1k zlib/1.2.11


Test environment used for verification: Debian-based VM (local build or system curl), pyftpdlib test FTP server (pyftpdlib 2.1.0 used in my tests).

Steps To Reproduce:

Only run these steps in an environment you control.

Prepare local test directories and files:

mkdir -p ~/curl-test/dir ~/curl-test/testdir
echo "INSIDE" > ~/curl-test/dir/inside.txt
echo "OUTSIDE" > ~/curl-test/testdir/outside.txt
cd ~/curl-test


Start a simple local FTP server in that directory (example):

# If not installed: python3 -m pip install pyftpdlib
python3 -m pyftpdlib -w
# Server serves current directory on 127.0.0.1:2121 (or default FTP port)


Run curl with trace enabled using an URL that contains an encoded traversal adjacent to repeated slashes:

curl --trace-ascii curl_trace.txt -v "ftp://127.0.0.1:2121/dir//%2e%2e/testdir" 2>&1 | tee curl_stdout.txt


Inspect curl_trace.txt for the CWD commands. Example trace excerpt (observed locally):

> CWD dir
< 250 "/dir" is the current directory.
> CWD ..
< 250 "/" is the current directory.


This shows that %2e%2e was decoded to .. and libcurl attempted CWD ...

Supporting Material/References:

All evidence provided inline as text (no attachments).

Trace excerpt (text-only):

> CWD dir
< 250 "/dir" is the current directory.
> CWD ..
< 250 "/" is the current directory.


Relevant local code pointers (text-only):

/* url-decode ftp path before further evaluation */
result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL);

/* parse the URL path into separate path components */
while(dirAlloc--) {
const char *spos = strchr(curPos, '/');
size_t clen = spos - curPos;
if(!clen && (ftpc->dirdepth == 0))
++clen;
/* we skip empty path components, like "x//y" ... */
if(clen) {
ftpc->dirs[ftpc->dirdepth].start = (int)(curPos - rawPath);
ftpc->dirs[ftpc->dirdepth].len = (int)clen;
ftpc->dirdepth++;
}
curPos = spos + 1;
}

/* sink: later used to send CWD */
result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);

## Impact

Path Traversal (CWE-22) — Improper Input Validation (CWE-20)

Overview:
Because libcurl decodes URL-encoded path segments before canonicalization and then accepts decoded .. components as valid path elements, an attacker who controls an FTP URL may cause libcurl to issue CWD .. commands. This client-side behavior can be leveraged in realistic attack scenarios where libcurl is used by applications for automated FTP fetches.

Realistic attack scenarios & consequences:

Remote file disclosure (Confidentiality): An attacker can craft a URL that causes libcurl to traverse to parent directories and attempt RETR on files outside the intended directory. In automated downloaders, update fetchers, backup/restore scripts, or content importers that accept external FTP URLs, this may expose sensitive configuration files, credentials, or private data.

Example: ftp://victim-server/uploads/%2e%2e/private/secret.cfg may lead the client to CWD uploads → CWD .. → RETR private/secret.cfg depending on server behavior and client usage.

Bypass of client-side filters (Integrity/Authorization): Applications that perform naive sanitization (e.g., simply searching for literal ..) can be bypassed by sending encoded equivalents (%2e%2e) because decoding occurs before canonicalization.

Supply chain & automation abuse: Software that automatically processes FTP URLs (e.g., CI/CD, synchronization tools) could be tricked into fetching or overwriting files outside the permitted area, leading to broader system compromise or corruption.

Variance by server implementation: The concrete impact depends on the FTP server’s path handling. Some servers may ignore .., some may enforce chroot-like restrictions, and some may honor CWD ... If the server honors CWD .. and the client has permissions to access files above the target directory, the impact is higher.

Exploitability / Preconditions:

Attacker must be able to supply an FTP URL that is processed by an application using libcurl (this is common in automated workflows or user-provided URL features).

No special authentication is required in the core logic: if the client is unauthenticated and the server allows directory changes, it may be exploitable unauthenticated; otherwise, impact is limited by authentication and server configuration.

Severity & CVSS guidance (estimate):

Suggested severity: High.

CVSS example vector (estimate for remote readability, no privileges, no user interaction):
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N → CVSS ~7.x
(Adjust upward if unauthenticated access to highly sensitive data is possible.)

Why this is significant:
This is not merely a formatting bug — it is a parsing/normalization logic gap that can be reliably triggered by encoded input. Many applications implicitly trust libcurl for correct path handling; if libcurl issues unintended CWD/RETR commands based on crafted URLs, confidential files could be exposed or automation workflows abused.

Suggested remediation summary (recap):

Perform canonical path normalization after URL-decoding and before splitting into path components (stack-based . / .. handling, collapse //).

Reject paths that attempt to traverse above the allowed root for absolute paths (return an error instead of sending CWD).

Add unit tests for encoded traversal cases and mixed permutations.
Visit Original Source

Basic Information

ID H1:3418861
Published Nov 10, 2025 at 19:43
Modified Nov 11, 2025 at 16:16

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.