Zip Slip in Aspnet
How Zip Slip Manifests in Aspnet
Zip Slip vulnerabilities in Aspnet applications typically occur when developers handle file uploads or archive extraction without proper path validation. The attack exploits the way Aspnet processes file paths, allowing attackers to write files outside the intended directory structure.
The most common Aspnet-specific scenario involves accepting ZIP files from users, extracting them to a web-accessible directory, and serving those files. Consider this vulnerable Aspnet controller action:
[HttpPost("upload")]
public async Task Upload(IFormFile file)
{
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/uploads");
using (var fileStream = new FileStream(Path.Combine(uploadsPath, file.FileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
return Ok();
} This code is vulnerable because it trusts the FileName property from the uploaded file. An attacker can craft a ZIP with entries like "../../web.config" or "../../../../bin/passwords.txt" and overwrite critical Aspnet configuration files.
Another Aspnet-specific manifestation occurs in middleware that processes uploaded archives. Aspnet's built-in file handling doesn't sanitize paths by default:
public class ArchiveHandlerMiddleware
{
private readonly RequestDelegate _next;
public async Task InvokeAsync(HttpContext context)
{
var file = context.Request.Form.Files[0];
var archive = new ZipArchive(file.OpenReadStream());
var targetPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
foreach (var entry in archive.Entries)
{
var fullPath = Path.Combine(targetPath, entry.FullName);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
entry.ExtractToFile(fullPath);
}
}
}The critical vulnerability here is Path.Combine() doesn't validate that the resulting path stays within the intended directory. An entry with "../../web.config" will resolve to a path outside wwwroot, allowing file overwrite.
Aspnet's Web Forms applications face similar risks when using the FileUpload control without path validation. The ViewState mechanism can also be abused to manipulate file paths if combined with Zip Slip, creating complex attack chains specific to Aspnet's architecture.
Aspnet-Specific Detection
Detecting Zip Slip in Aspnet applications requires examining both code patterns and runtime behavior. Static analysis tools can identify vulnerable Path.Combine() usage, but runtime scanning with middleBrick provides comprehensive coverage.
middleBrick's black-box scanning approach tests the unauthenticated attack surface by submitting crafted ZIP files with path traversal payloads. The scanner analyzes the response to determine if files were written outside the intended directory.
Key detection patterns for Aspnet include:
- Path traversal in ZIP entries: ../../web.config, ../../../bin/aspnetcore.dll
- Windows-specific attacks: ..\..\web.config, ..\..\bin\aspnetcore.dll
- UNC path injection: \\localhost\c$\inetpub\wwwroot\web.config
- Absolute path injection: /etc/passwd, C:\Windows\System32\drivers\etc\hosts
middleBrick tests these patterns automatically and reports the severity based on the impact. Overwriting web.config files in Aspnet applications can lead to remote code execution, making this a critical finding.
Code-level detection using Roslyn analyzers can identify vulnerable patterns:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ZipSlipAnalyzer : DiagnosticAnalyzer
{
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzePathCombine, SyntaxKind.InvocationExpression);
}
private void AnalyzePathCombine(SyntaxNodeAnalysisContext context)
{
var invocation = (InvocationExpressionSyntax)context.Node;
if (invocation.Expression is not IdentifierNameSyntax identifier ||
identifier.Identifier.Text != "Path.Combine")
return;
var arguments = invocation.ArgumentList.Arguments;
foreach (var arg in arguments)
{
if (arg.Expression is LiteralExpressionSyntax literal &&
literal.Token.Value is string pathValue &&
pathValue.Contains("..") || pathValue.Contains("\\"))
{
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor("ZIP001", "Potential Zip Slip",
"Path.Combine with untrusted input may allow path traversal",
"Security", DiagnosticSeverity.Error, true),
invocation.GetLocation()));
}
}
}
}Runtime detection can also monitor file system access patterns to identify suspicious writes outside allowed directories, though this requires instrumentation beyond middleBrick's scope.
Aspnet-Specific Remediation
Remediating Zip Slip in Aspnet applications requires both input validation and safe file handling practices. The most effective approach combines path sanitization with directory confinement.
First, implement path validation before any file operations:
public static class PathValidator
{
public static string GetValidatedPath(string basePath, string untrustedPath)
{
var fullPath = Path.GetFullPath(Path.Combine(basePath, untrustedPath));
if (!fullPath.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Path traversal attempt detected");
}
return fullPath;
}
}Apply this validator to your file upload handlers:
[HttpPost("upload")]
public async Task Upload(IFormFile file)
{
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/uploads");
var validatedPath = PathValidator.GetValidatedPath(uploadsPath, file.FileName);
using (var fileStream = new FileStream(validatedPath, FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
return Ok();
} For ZIP file processing, validate each entry before extraction:
public class SafeArchiveHandler
{
public void ExtractArchive(IFormFile archiveFile, string targetDirectory)
{
using (var archiveStream = archiveFile.OpenReadStream())
using (var archive = new ZipArchive(archiveStream))
{
foreach (var entry in archive.Entries)
{
var validatedPath = PathValidator.GetValidatedPath(
targetDirectory, entry.FullName.Replace('/', Path.DirectorySeparatorChar));
Directory.CreateDirectory(Path.GetDirectoryName(validatedPath));
entry.ExtractToFile(validatedPath);
}
}
}
}Aspnet's hosting model provides additional protection through the content root and web root configuration. Always extract files to a dedicated directory outside the web root when possible:
public class ArchiveController : Controller
{
private readonly IWebHostEnvironment _env;
public ArchiveController(IWebHostEnvironment env)
{
_env = env;
}
[HttpPost("extract")]
public async Task ExtractArchive(IFormFile archive)
{
var tempPath = Path.Combine(_env.ContentRootPath, "App_Data", "extracted");
Directory.CreateDirectory(tempPath);
using (var archiveStream = archive.OpenReadStream())
using (var zip = new ZipArchive(archiveStream))
{
foreach (var entry in zip.Entries)
{
var targetPath = PathValidator.GetValidatedPath(tempPath, entry.FullName);
if (string.IsNullOrEmpty(entry.Name))
{
Directory.CreateDirectory(targetPath);
}
else
{
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
entry.ExtractToFile(targetPath);
}
}
}
return Ok();
}
} For production deployments, consider using a library that automatically validates paths, such as SharpZipLib with custom validation, or implement a whitelist of allowed file extensions and directory structures.