Description
Using ZIP entry names as extraction paths without validation can allow entries containing ../ to write files outside the destination directory.
Potential impact
- Arbitrary file writes, overwritten configuration, or file creation in a web root.
Remediation
Compute each entry's final canonical path and reject extraction if it escapes the destination directory. Create a new extraction directory beneath a service-owned location attackers cannot write to, and do not overwrite existing files.
Examples
Before
csharp
using System.IO;
using System.IO.Compression;
sealed class ZipExtractor
{
public static void ExtractEntry(
ZipArchiveEntry entry,
string targetDirectory)
{
var path = Path.GetFullPath(Path.Combine(targetDirectory, entry.FullName));
entry.ExtractToFile(path, overwrite: true);
}
}
After
csharp
using System;
using System.IO;
using System.IO.Compression;
static string ExtractToNewDirectory(ZipArchive archive, string serviceOwnedRoot)
{
// Other users or processes must not be able to write to serviceOwnedRoot.
var targetRoot = Directory.CreateDirectory(
Path.Combine(serviceOwnedRoot, Path.GetRandomFileName())).FullName;
var targetPrefix = Path.GetFullPath(targetRoot) + Path.DirectorySeparatorChar;
foreach (var entry in archive.Entries)
{
var path = Path.GetFullPath(Path.Combine(targetRoot, entry.FullName));
if (!path.StartsWith(targetPrefix, StringComparison.Ordinal))
{
throw new InvalidDataException("Archive entry escapes the target directory");
}
if (string.IsNullOrEmpty(entry.Name))
{
Directory.CreateDirectory(path);
continue;
}
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
entry.ExtractToFile(path, overwrite: false);
}
return targetRoot;
}
Explanation:
- Before: Without validating the normalized destination, an entry containing
../can write outside the target directory. - After: Each extraction uses a new directory in a service-only location. The
Ordinalboundary check prevents path escape on case-sensitive filesystems as well, whileoverwrite: falsepreserves existing files.