Developers relying on the basic-ftp npm package version 5.2.0 face a high-severity vulnerability: remote FTP command injection through CRLF sequences in file paths. Attackers can split legitimate FTP commands into arbitrary ones by embedding \r\n in parameters to methods like cd(), remove(), rename(), uploadFrom(), downloadTo(), list(), and removeDir(). CVSS score sits at 8.6 (High), with a vector of CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L. No patch exists as of April 4, 2026.
This flaw stems from poor path handling in a library with over 100,000 weekly downloads on npm. FTP remains in use for legacy systems, file transfers in CI/CD pipelines, and some cloud services despite its insecurity—no encryption, plain-text creds. basic-ftp, a lightweight Node.js client, promised simplicity but skips basic input validation here. Attackers need only control a path string, common in user-facing apps or misconfigured integrations.
Root Cause: Flimsy Sanitization Meets Direct Socket Writes
The bug combines two weaknesses. First, protectWhitespace() in dist/Client.js (line 677) checks only for leading spaces:
async protectWhitespace(path) {
if (!path.startsWith(" ")) {
return path; // No check for \r\n anywhere
}
const pwd = await this.pwd();
const absolutePathPrefix = pwd.endsWith("/") ? pwd : pwd + "/";
return absolutePathPrefix + path;
}
It returns attacker input unchanged if no leading space, preserving embedded \r (0x0D) or \n (0x0A). Second, FtpContext.send() (line 177) appends \r\n and writes directly to the socket:
send(command) {
this._socket.write(command + "\r\n", this.encoding);
}
A path like ../../etc/passwd\r\nDELE /important/file turns DELE ../../etc/passwd into DELE ../../etc/passwd followed by DELE /important/file. FTP servers parse each \r\n as a new command boundary.
This matches CWE-93 (CRLF Injection), a classic in protocols like HTTP (think HTTP Response Splitting) and SMTP. FTP’s command-response model amplifies it—no framing or quoting protects the control channel.
Attack Scenarios and Why They Matter
Impacts go beyond theory. If your app processes untrusted paths—say, from URLs, configs, or APIs—an attacker injects commands post-authentication:
- Delete files:
DELE /var/log/apache2/access.log\r\nDELE /etc/shadow. - Recon:
LIST /\r\nPWD\r\nSYSTto map the server. - Exfiltrate:
RETR /private/keys/id_rsaduring a legit download. - Escalate on misconfigured servers:
SITE EXEC rm -rf /if enabled (rare but exists in ProFTPD, vsftpd variants). - Hijack sessions:
USER root\r\nPASS rootto switch users mid-session.
Real stakes: Compromised FTP servers often hold sensitive data—backups, certs, source code. In supply chains, a vuln lib like this ripples: npm audit misses it without Snyk or similar scans. Node.js apps in Docker, Lambda, or Vercel functions calling FTP amplify reach. CVSS underrates integrity (I:H) since deletion hits availability too.
Skeptical note: basic-ftp’s maintainer released 5.2.0 recently, but no fix after disclosure? Last commits suggest spotty maintenance. Compare to mature libs like Apache Commons Net, which quote paths properly.
Mitigate Now: Ditch or Defend
Audit your deps: npm ls basic-ftp. If 5.2.0, pin to 5.1.x or fork with a protectWhitespace() fix—strip [\r\n] via path.replace(/[\r\n]/g, ''). Better: Switch to ftp (parse-ftp) or ssh2-sftp-client for SFTP, ditching FTP entirely. SFTP encrypts and lacks CRLF issues.
npm uninstall basic-ftp
npm install ssh2-sftp-client
Short-term: Proxy paths server-side, validate against whitelists. Test with a PoC: client.remove('test\r\nDELE dummy') on a throwaway server. Why act fast? FTP vulns fuel breaches—recall 2021 ProFTPD mod_rack exploits. In 2024’s post-Log4Shell world, unpatched deps are low-hanging fruit for attackers scanning Shodan for FTP ports (millions exposed).
This matters because FTP lingers in 20% of file transfer workflows per surveys. One injected command cascades to data loss or RCE. Prioritize: Scan, replace, monitor. No excuses for 2026.