How Path Traversal in basic-ftp Leads to File Overwrite Attacks and How to Fix It
Introduction
In a Node.js application using the basic-ftp library, a critical vulnerability lurked in the dependency tree: CVE-2026-27699, a path traversal flaw that could allow attackers to overwrite arbitrary files on the system. The vulnerability existed in basic-ftp version 5.1.0, which was referenced in package-lock.json as a direct or transitive dependency. Unlike many vulnerabilities that require complex exploitation chains, this one was deceptively simple—an attacker could craft a malicious FTP filename containing directory traversal sequences like ../ and bypass the library's file path validation, potentially overwriting critical application files, configuration files, or even system binaries.
The fix was straightforward but critical: upgrade basic-ftp from 5.1.0 to 5.3.1 and enforce this version across all transitive dependencies using npm overrides. This blog post dissects the vulnerability, explains the attack mechanism, and demonstrates how the fix eliminates the risk.
The Vulnerability Explained
What Went Wrong in basic-ftp 5.1.0
Path traversal vulnerabilities in file operations occur when an application fails to properly validate file paths before performing operations like read, write, or delete. In the case of basic-ftp 5.1.0, the library accepted file paths from FTP commands without sufficiently sanitizing them.
Consider a typical FTP scenario: a user initiates an FTP connection and downloads a file named document.pdf. The basic-ftp library handles the file path and writes it to disk. But what if an attacker sends a specially crafted filename like:
../../../../etc/passwd
or
../../../config/database.yml
In version 5.1.0, the library did not properly validate these paths. Instead of rejecting the traversal sequences or resolving the canonical path to verify it remained within the intended directory, basic-ftp would process the path as-is, allowing the attacker to:
- Overwrite application configuration files (e.g.,
.env,config.json) - Replace application code (e.g.,
index.js, middleware files) - Corrupt database files or other critical data
- Inject malicious code that would be executed on the next application restart
Attack Scenario: Real-World Impact
Imagine a Node.js web application that uses basic-ftp to download files from a corporate FTP server:
// Vulnerable code pattern (basic-ftp 5.1.0)
const Client = require('basic-ftp').Client;
async function downloadFile(filename) {
const client = new Client();
await client.access(ftpConfig);
await client.downloadTo(`./uploads/${filename}`, filename);
await client.close();
}
// Attacker calls: downloadFile('../../../src/app.js')
// Result: The file is downloaded and saved to ./src/app.js, overwriting the main app file
An attacker on the FTP server (or intercepting the connection) could provide a filename like ../../../src/app.js. Because basic-ftp 5.1.0 didn't validate the path, it would:
- Accept the traversal sequence
- Resolve the path to
./src/app.js(outside the intended./uploads/directory) - Overwrite the actual application file
On the next application restart, the malicious code would execute with full application privileges.
Why This Is Critical
The CVSS score for this vulnerability is CRITICAL because:
- Easy to exploit: No special tools or deep technical knowledge required; just craft a filename
- High impact: Can lead to remote code execution (RCE), data exfiltration, or complete system compromise
- Wide reach: Any application using basic-ftp for file downloads is affected
- Supply chain risk: The vulnerability exists in the dependency tree, affecting applications that don't directly use basic-ftp but depend on packages that do (like
firebase-tools,get-uri,pac-proxy-agent, andproxy-agent)
The Fix
What Changed: Upgrade from 5.1.0 to 5.3.1
The fix involved two key changes reflected in the PR:
1. Primary Change: Upgrade basic-ftp in package-lock.json
"node_modules/basic-ftp": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz",
- "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==",
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz",
+ "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
This upgrade ensures that when dependencies are installed, version 5.3.1 (with path validation fixes) is used instead of the vulnerable 5.1.0.
2. Secondary Change: Add npm Overrides in package.json
"overrides": {
"basic-ftp": {
"basic-ftp": "5.3.1"
},
"firebase-tools": {
"basic-ftp": "5.3.1"
},
"get-uri": {
"basic-ftp": "5.3.1"
},
"pac-proxy-agent": {
"basic-ftp": "5.3.1"
},
"proxy-agent": {
"basic-ftp": "5.3.1"
}
}
This is the critical security improvement. Even though the application might not directly depend on basic-ftp, packages like firebase-tools, get-uri, pac-proxy-agent, and proxy-agent do. These transitive dependencies might declare older versions of basic-ftp in their own package.json files.
The overrides field in npm (available in npm 8.3.0+) forces all these packages to use the patched version 5.3.1, regardless of what their individual package.json files specify. This prevents a situation where:
Your app → firebase-tools → basic-ftp 5.1.0 (vulnerable!)
Without overrides, you'd need to wait for firebase-tools to update their basic-ftp dependency, which could take weeks or months. With overrides, the fix is immediate.
How basic-ftp 5.3.1 Prevents the Attack
In version 5.3.1, the basic-ftp library implements proper path validation:
- Canonical path resolution: Converts paths like
../../../src/app.jsto their absolute, canonical form - Boundary verification: Ensures the resolved path remains within the intended directory (e.g.,
./uploads/) - Rejection of traversal sequences: Blocks or sanitizes dangerous patterns like
../,..\\, and absolute paths
The exact implementation in basic-ftp 5.3.1 likely includes logic similar to:
// Conceptual fix in basic-ftp 5.3.1
const path = require('path');
function validateFilePath(filename, baseDir) {
// Resolve to absolute path
const resolvedPath = path.resolve(baseDir, filename);
const resolvedBase = path.resolve(baseDir);
// Ensure resolved path is within baseDir
if (!resolvedPath.startsWith(resolvedBase + path.sep) && resolvedPath !== resolvedBase) {
throw new Error(`Path traversal detected: ${filename}`);
}
return resolvedPath;
}
Now, if an attacker provides ../../../src/app.js, the validation would:
- Resolve it:
/absolute/path/to/src/app.js - Check if it's within
/absolute/path/to/uploads/: NO - Reject the request with an error
Prevention & Best Practices
For Developers Using FTP Libraries
-
Always validate file paths: Never trust filenames from external sources (FTP servers, user uploads, API parameters)
-
Use path resolution and boundary checking:
```javascript
const path = require('path');
const fs = require('fs');
function safeDownload(filename, baseDir) {
const resolved = path.resolve(baseDir, filename);
const base = path.resolve(baseDir);
if (!resolved.startsWith(base)) {
throw new Error('Invalid path');
}
return resolved;
}
```
-
Keep dependencies updated: Regularly run
npm auditand update vulnerable packages
bash npm audit npm update -
Use security scanning tools: Integrate Snyk, Trivy, or similar tools into your CI/CD pipeline to catch vulnerable dependencies before they reach production
-
Implement dependency overrides: When transitive dependencies have vulnerabilities, use npm overrides to enforce patched versions across your entire dependency tree
Security Standards & References
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory
- OWASP: Path Traversal
- CAPEC: CAPEC-126: Path Traversal
Tools for Detection
- Semgrep: Detects path traversal patterns and unsafe file operations
- Trivy: Container and dependency scanner that identified CVE-2026-27699
- npm audit: Built-in vulnerability scanner for Node.js projects
- Snyk: Continuous security monitoring for open source dependencies
Key Takeaways
- Path traversal in basic-ftp 5.1.0 allowed attackers to overwrite arbitrary files by injecting
../sequences into filenames, bypassing directory restrictions - The fix (upgrading to 5.3.1) implements canonical path resolution and boundary validation, ensuring that file operations cannot escape the intended directory
- Transitive dependencies are a critical attack surface: The vulnerability existed not just in direct dependencies but in packages like
firebase-tools,get-uri,pac-proxy-agent, andproxy-agent - npm overrides are essential for supply chain security: Without them, you'd be blocked waiting for upstream packages to update their dependencies
- Always validate external input in file operations: Never assume that filenames, paths, or URLs from external sources are safe; always canonicalize and verify boundaries
How Orbis AppSec Detected This
Source: File path input from FTP operations in basic-ftp library functions
Sink: File write operations in basic-ftp 5.1.0 that process user-supplied filenames without proper validation
Missing control: The vulnerable version lacked canonical path resolution and boundary verification to ensure file operations remained within intended directories
CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
Fix: Upgrade basic-ftp from 5.1.0 to 5.3.1 and enforce this version across all transitive dependencies using npm overrides
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
CVE-2026-27699 demonstrates how a seemingly small validation gap in a file operation can become a critical security vulnerability affecting entire supply chains. The path traversal flaw in basic-ftp 5.1.0 could have allowed attackers to overwrite application code, configuration files, or system files, leading to remote code execution or data corruption.
The fix—upgrading to version 5.3.1 and enforcing this version across transitive dependencies—is straightforward but essential. More importantly, it highlights the importance of:
- Treating file path validation seriously: Always canonicalize paths and verify boundaries
- Managing transitive dependencies: Use npm overrides to enforce security fixes across your entire dependency tree
- Staying vigilant with security updates: Regularly audit dependencies and integrate security scanning into your CI/CD pipeline
By understanding how this vulnerability worked and how the fix eliminates it, you're better equipped to spot similar issues in your own code and make your applications more resilient to path traversal attacks.