Security Misconfiguration

Security Misconfiguration in NestJS: File Upload RCE [CVE-2024-29409]

[Updated month year] Updated CVE-2024-29409

Overview

The CVE-2024-29409 vulnerability describes a file upload flaw in NestJS v10.3.2 where an attacker can achieve remote code execution by exploiting how the app handles the Content-Type header. Classified under CWE-94 (Code Injection), this misconfiguration arises when server-side upload logic trusts the client-provided Content-Type to authorize or process uploaded files. If the upload path, file naming, or subsequent processing relies on user-supplied headers or filenames, an attacker can craft a payload (for example a JavaScript payload) and trick the server into treating it as executable code. In real deployments, this can lead to arbitrary code execution, server compromise, and a foothold for broader attacks, especially if uploaded files are later loaded, required, or executed by the application. In practice, an attacker would target an endpoint that accepts uploads and uses the Content-Type header to gate the upload or to name and store the file. By spoofing Content-Type and presenting a crafted file, the server may store the payload under a name or path that is later used in code execution paths, or may bypass type checks that would otherwise reject dangerous content. This misconfiguration is particularly dangerous in frameworks like NestJS when file handling is not strictly validated and when uploaded content can influence runtime behavior. The root cause is trusting client-supplied headers to enforce security decisions, which falls under a code injection risk (CWE-94) and leads to Security Misconfiguration if not corrected. To mitigate, treat file uploads as untrusted input and perform server-side validation independent of Content-Type. In NestJS, rely on server-detected mime types (file.mimetype) and robust allowlists, sanitize file names, and isolate upload storage away from executable paths. Do not load or execute uploaded code. Use strict file filters, content inspection, and safe storage practices. Keep dependencies patched and add tests that simulate spoofed headers and malicious payloads to ensure the application rejects them. This combination of validation, sanitization, and safe handling closes the gap that enables this class of vulnerability (CWE-94) in NestJS deployments.

Affected Versions

NestJS v10.3.2

Code Fix Example

NestJS API Security Remediation
/* Vulnerable pattern: relying on Content-Type header and unsanitized filenames (allows RCE via crafted payload) */
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';

@Controller('upload')
export class UploadController {
  @Post()
  @UseInterceptors(FileInterceptor('file', {
    storage: diskStorage({
      destination: './uploads',
      filename: (req, file, cb) => {
        // Using user-provided originalname directly can lead to path traversal or executable exposure
        cb(null, file.originalname);
      }
    }))
  })
  upload(@UploadedFile() file: Express.Multer.File) {
    // Vulnerable: no validation of file type; potential path traversal and later execution risk
    // e.g., an uploaded .js file could be loaded/executed by the app if unsafe code paths exist
  }
}

/* Fixed pattern: validate mime type and extension, sanitize filename, and avoid executing uploads */
import { Controller, Post, UploadedFile, UseInterceptors, BadRequestException } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import path from 'path';

const allowedMime = ['image/png', 'image/jpeg', 'application/pdf', 'text/plain'];
const allowedExt = ['.png', '.jpg', '.jpeg', '.gif', '.pdf', '.txt'];

const fileFilter = (req, file, cb) => {
  const mime = file.mimetype;
  const ext = path.extname(file.originalname || '').toLowerCase();
  if (allowedMime.includes(mime) && allowedExt.includes(ext)) {
    cb(null, true);
  } else {
    cb(new BadRequestException('Unsupported file type'), false);
  }
};

const storage = diskStorage({
  destination: './uploads',
  filename: (req, file, cb) => {
    // Sanitize the filename and prefix with a timestamp to prevent overwrites and path traversal
    const safeName = path.basename(file.originalname).replace(/\s+/g, '_');
    cb(null, `${Date.now()}_${safeName}`);
  }
});

@Controller('upload')
export class UploadControllerFixed {
  @Post()
  @UseInterceptors(FileInterceptor('file', { storage, fileFilter }))
  upload(@UploadedFile() file: Express.Multer.File) {
    // Safely stored file. Do not execute or require uploaded content. Treat as data only.
  }
}

CVE References

Choose which optional cookies to allow. You can change this any time.