Dangerous File Upload

ID

csharp.dangerous_file_upload

Severity

critical

Remediation Complexity

medium

Remediation Risk

medium

Remediation Effort

medium

Resource

Path Resolution

Language

CSharp

Tags

ASVS50:v5.2.2, CWE:434, NIST.SP.800-53, OWASP:2025:A02, OWASP:2025:A06, PCI-DSS:6.5.8

Description

An ASP.NET Core action receives an uploaded file as an IFormFile (or a collection of them) and persists it to disk — or exposes its stream — without validating the file extension or content type. Accepting arbitrary uploads with no restriction on type, extension or destination can let an attacker place executable or otherwise dangerous content on the server.

Rationale

Unrestricted file upload (CWE-434) is a well-known attack vector. When the uploaded file’s name, extension and content type are trusted as-is, an attacker can upload a file with a dangerous extension (for example a server-executable script or a double extension), overwrite existing files, or store content that is later served back and executed. Persisting the raw upload (CopyTo, CopyToAsync, OpenReadStream, SaveAs) before any check is what makes the endpoint exploitable.

[ApiController]
[Route("api/[controller]")]
public class UploadController : ControllerBase
{
    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        // VULNERABLE: no extension / content-type validation before saving
        var path = Path.Combine("uploads", file.FileName);
        using var stream = System.IO.File.Create(path);
        await file.CopyToAsync(stream);
        return Ok();
    }
}

Remediation

Validate the upload before persisting it: check the file extension against an allow-list of permitted extensions, verify the ContentType, cap the file size, and store the file under a server-generated name in a non-executable location.

[ApiController]
[Route("api/[controller]")]
public class UploadController : ControllerBase
{
    private static readonly string[] Permitted = { ".png", ".jpg", ".pdf" };

    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
        if (string.IsNullOrEmpty(ext) || !Permitted.Contains(ext))
        {
            return BadRequest("File type not allowed.");
        }
        if (file.ContentType != "image/png" && file.ContentType != "image/jpeg" && file.ContentType != "application/pdf")
        {
            return BadRequest("Content type not allowed.");
        }

        var safeName = Guid.NewGuid().ToString() + ext;
        var path = Path.Combine("uploads", safeName);
        using var stream = System.IO.File.Create(path);
        await file.CopyToAsync(stream);
        return Ok();
    }
}