Jan 16, 2026·6 min read·68 visits
By sending a specifically crafted URL containing encoded dot segments, an attacker can trick the Vert.x web server into caching a '404 Not Found' response for a legitimate file. Subsequent requests from valid users will receive the cached 404, effectively making the file vanish from the web until the cache expires or the server restarts.
A logic error in URI normalization allows unauthenticated attackers to poison the Vert.x StaticHandler cache, causing persistent Denial of Service for legitimate files.
Eclipse Vert.x is the darling of the Java reactive world. It's fast, non-blocking, and designed to handle massive concurrency without breaking a sweat. Developers love it because it feels like driving a Ferrari compared to the minivan that is a traditional servlet container.
But here is the thing about high-performance machinery: the tighter you tune the engine, the more catastrophic a loose bolt becomes. In this case, the loose bolt wasn't in the complex event loop or the networking stack. It was in the most boring place imaginable: the StaticHandler, the component responsible for serving your CSS, JS, and HTML files.
This vulnerability isn't about crashing the server with a buffer overflow. It's more subtle and arguably more annoying. It's a cache poisoning attack that allows an attacker to tell the server, "Hey, this file doesn't exist," and the server believes it so hard that it refuses to serve the file to anyone else. It is like convincing a library that a book is missing so they delete the catalogue entry while the book is sitting right on the shelf.
The root cause lies in how Vert.x handles URI normalization. When a web server receives a request for /foo/../bar, it needs to simplify that path to /bar before looking it up on the disk. This logic is governed by RFC 3986, specifically the "Remove Dot Segments" algorithm (Section 5.2.4).
RFCs are notoriously dry, and implementing them is like trying to follow IKEA instructions written in ancient Sumerian. Vert.x implemented a utility called HttpUtils.removeDots to handle this. One specific rule (Rule C) states that if a path segment ends in .., you should remove that segment and the one preceding it.
The flaw was a classic off-by-one logic error. The algorithm tries to find the previous path segment to delete it. Usually, segments are separated by slashes (/). The vulnerable code looked for the last slash in the output buffer to decide what to delete. But what happens if you are at the very start of the buffer and there is no preceding slash?
> [!NOTE] > The code assumed a slash always existed to demarcate segments. When that assumption failed, the code did... absolutely nothing. It skipped the deletion entirely.
Let's look at the smoking gun in io.vertx.core.http.impl.HttpUtils.java. This is where the cleanup happens. The developer wanted to remove the last segment from obuf (the output buffer).
Here is the vulnerable code:
int pos = obuf.lastIndexOf("/");
if (pos != -1) {
// If we found a slash, delete from there to the end
obuf.delete(pos, obuf.length());
}
// If pos WAS -1, we did nothing. Oops.If obuf contained segment (no slash), pos returns -1. The if block is skipped. The dot segment isn't removed. The path remains dirty.
Here is the fix introduced in commit d007e7b418543eb1567fe95cf20f5450a5c2d047:
int pos = obuf.lastIndexOf("/");
// If pos is -1, it means the whole buffer is the segment to remove.
// So set length to 0. Otherwise, set it to the slash position.
obuf.setLength(pos == -1 ? 0 : pos);This tiny change—handling the -1 case by clearing the buffer—closes the loophole. It ensures that even the first segment in a path gets nuked if a dot-dot follows it.
So how do we turn a path normalization bug into a Denial of Service? We use the StaticHandler's caching mechanism against itself. By default, Vert.x caches the results of file lookups to speed up responses. This includes caching the fact that a file was not found (404).
The attack vector relies on sending a URI that confuses the normalizer but is technically valid enough to pass through the router.
The Attack Chain:
http://target.com/assets/style.css.http://target.com/assets/nonexistent%2F..%2Fstyle.css.
%2F. This trick causes the router to treat nonexistent/../ as part of the filename initially.removeDots function fails to strip the segment correctly due to the bug described above.style.css entry due to the normalization discrepancy.Now, when a legitimate user requests http://target.com/assets/style.css, the cache intercepts the request and says: "Oh, I remember this one! It doesn't exist." The user gets a 404.
Let's be clear: this isn't Remote Code Execution (RCE). We aren't popping shells or stealing database credentials. However, in a business context, availability is security.
Imagine an e-commerce site where the checkout.js file suddenly returns a 404 for every customer. The backend is fine, the database is fine, but the frontend is broken. No one can buy anything.
The persistence is the killer here. In many DoS attacks, you stop the traffic, and the service recovers. Here, the service is "poisoned." The 404 remains in the Local Least Recently Used (LRU) cache until:
An attacker needs to send only one packet per file to take it offline. It is extremely efficient.
The remediation is straightforward. The Eclipse team patched this in Vert.x versions 4.5.24 and 5.0.7. If you are running anything older, your static assets are sitting ducks.
If you cannot update immediately because of "enterprise reasons" (we know the drill), you can apply a workaround. You must disable caching in the StaticHandler.
// The "I can't patch yet" workaround
StaticHandler handler = StaticHandler.create()
.setCachingEnabled(false); // Goodbye performance, hello safetyThis stops the bleeding by preventing the bad 404 result from sticking, but it turns your high-performance event loop into a dumb file reader that hits the disk for every single request. Patching is highly recommended.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:L| Product | Affected Versions | Fixed Version |
|---|---|---|
Eclipse Vert.x Eclipse Foundation | >= 4.0.0, <= 4.5.23 | 4.5.24 |
Eclipse Vert.x Eclipse Foundation | >= 5.0.0, <= 5.0.6 | 5.0.7 |
| Attribute | Detail |
|---|---|
| CWE | CWE-444 (Inconsistent Interpretation of HTTP Requests) |
| CVSS v4.0 | 6.9 (Medium) |
| Attack Vector | Network (Unauthenticated) |
| Impact | Denial of Service (Cache Poisoning) |
| Fix Commit | d007e7b418543eb1567fe95cf20f5450a5c2d047 |
| Protocol | HTTP/1.1, HTTP/2 |
The application interprets HTTP requests inconsistently compared to intermediaries or standards, leading to cache poisoning or request smuggling.
A property shadowing vulnerability exists in protobufjs where schema-derived names can collide with and overwrite runtime-critical internal helper properties. This issue leads to uncaught runtime exceptions and crash-based Denial of Service.
An integer truncation vulnerability (CWE-197) exists in SQLite before version 3.50.2 during the processing of aggregate queries with more than 32,767 distinct column references. This causes an internal 32-bit counter to truncate to a signed 16-bit integer, producing negative values that cause out-of-bounds heap operations in release builds.
An integer overflow vulnerability in the Windows kernel-mode HTTP driver (HTTP.sys) allows an unauthenticated remote attacker to execute arbitrary code with kernel privileges or cause a Denial of Service via a specially crafted sequence of HTTP request headers.
A memory corruption vulnerability exists in the FTS5 (Full-Text Search 5) extension of SQLite prior to version 3.53.2. An attacker can construct a malicious database file containing corrupt FTS5 page data. Querying this database triggers out-of-bounds reads and heap-based buffer overflows, potentially causing a crash or arbitrary code execution.
A mass assignment vulnerability (CWE-915) in n8n's self-service settings API endpoint (PATCH /me/settings) allows authenticated Single Sign-On (SSO) users to disable SSO enforcement for their accounts by injecting administrative parameters. This bypasses organizational identity provider controls and multi-factor authentication (MFA).
CVE-2026-55699 (also identified as GHSA-4gxm-v5v7-fqc4) is a critical path traversal and arbitrary directory deletion vulnerability in the pnpm package manager. The issue exists because the manifest validation process fails to prevent relative path segments within the package 'bin' keys. When a malicious package containing structured path traversal markers is globally installed and later manipulated, pnpm resolves the target paths through path.join() and passes the resolved paths to a recursive deletion function, resulting in arbitrary directory removal.