TechnitiumSoftware/DnsServer · error · IOException

Extracting Zip entry would have resulted in a file outside t

Error message

Extracting Zip entry would have resulted in a file outside the specified destination directory.

What it means

Thrown during both install and update extraction when a ZipArchiveEntry's full target path would land outside the application folder. This is the classic 'zip-slip' mitigation (CVE pattern): every entry is canonicalized and checked against the destination prefix.

Source

Thrown at DnsServerCore/Dns/Applications/DnsApplicationManager.cs:436

            string applicationFolder = Path.Combine(_appsPath, applicationName);

            //keep a copy of the zip file in the application folder for transferring to other nodes
            await using (FileStream zipCopyStream = new FileStream(Path.Combine(applicationFolder, applicationName + ".zip"), FileMode.Create, FileAccess.ReadWrite))
            {
                await appZipStream.CopyToAsync(zipCopyStream);

                zipCopyStream.Position = 0;

                await using (ZipArchive appZip = new ZipArchive(zipCopyStream, ZipArchiveMode.Read, false, Encoding.UTF8))
                {
                    UnloadApplication(applicationName);

                    foreach (ZipArchiveEntry entry in appZip.Entries)
                    {
                        string filePath = Path.GetFullPath(Path.Combine(applicationFolder, entry.FullName));
                        if (!filePath.StartsWith(applicationFolder + Path.DirectorySeparatorChar))
                            throw new IOException("Extracting Zip entry would have resulted in a file outside the specified destination directory.");

                        if ((entry.Name == "dnsApp.config") && File.Exists(filePath))
                            continue; //avoid overwriting existing config file

                        if ((entry.Length == 0) && (entry.Name.Length == 0) && entry.FullName.EndsWith('/'))
                        {
                            //directory entry
                            Directory.CreateDirectory(filePath);
                        }
                        else
                        {
                            //file entry
                            Directory.CreateDirectory(Path.GetDirectoryName(filePath));

                            await entry.ExtractToFileAsync(filePath, true);
                        }
                    }

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Inspect the zip's entry list before install and reject entries containing '..' or rooted paths.
  2. Re-package the app so all entries are relative to the application root with no parent traversal.
  3. Only install apps from trusted authors; verify the zip's directory structure after extraction tooling.
  4. If you control packaging, build with a tool that emits only relative, in-root entries (e.g. zip from inside the folder).

Example fix

// before
using var zip = new ZipArchive(stream, ZipArchiveMode.Read);
foreach (var e in zip.Entries) { /* trust entries */ }

// after
using var zip = new ZipArchive(stream, ZipArchiveMode.Read);
if (zip.Entries.Any(e => e.FullName.Contains("..") || Path.IsPathRooted(e.FullName)))
    throw new InvalidOperationException("Refusing zip with traversal/rooted entries");
Defensive patterns

Strategy: validation

Validate before calling

bool ZipIsSafe(Stream s)
{
    using var zip = new ZipArchive(s, ZipArchiveMode.Read, leaveOpen: true);
    return zip.Entries.All(e => !e.FullName.Contains("..") && !Path.IsPathRooted(e.FullName));
}

Type guard

static bool IsSafeZipEntry(string fullName) =>
    !fullName.Contains("..") && !Path.IsPathRooted(fullName);

Try / catch

try { await mgr.InstallApplicationAsync(name, zip); }
catch (IOException ex) when (ex.Message.Contains("outside the specified destination")) { log.Warn($"Rejected traversal zip for {name}"); }

Prevention

When it happens

Trigger: A zip containing an entry whose FullName uses ../ to climb out of applicationFolder (e.g. ../../etc/passwd), or an entry with an absolute/rooted path that resolves outside. Triggered by a malicious or poorly authored app package.

Common situations: Side-loaded third-party app zip; zip produced on Windows with backslash paths extracted on Linux (or vice versa) where separator normalization causes a mismatch; deliberately packaged app that escapes its dir.

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/232a9c393b63d405. Report an issue: GitHub.