{"record":{"id":"232a9c393b63d405","repo":"TechnitiumSoftware/DnsServer","slug":"extracting-zip-entry-would-have-resulted-in-a-file","errorCode":null,"errorMessage":"Extracting Zip entry would have resulted in a file outside the specified destination directory.","messagePattern":"Extracting Zip entry would have resulted in a file outside the specified destination directory\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Applications/DnsApplicationManager.cs","lineNumber":436,"sourceCode":"\n            string applicationFolder = Path.Combine(_appsPath, applicationName);\n\n            //keep a copy of the zip file in the application folder for transferring to other nodes\n            await using (FileStream zipCopyStream = new FileStream(Path.Combine(applicationFolder, applicationName + \".zip\"), FileMode.Create, FileAccess.ReadWrite))\n            {\n                await appZipStream.CopyToAsync(zipCopyStream);\n\n                zipCopyStream.Position = 0;\n\n                await using (ZipArchive appZip = new ZipArchive(zipCopyStream, ZipArchiveMode.Read, false, Encoding.UTF8))\n                {\n                    UnloadApplication(applicationName);\n\n                    foreach (ZipArchiveEntry entry in appZip.Entries)\n                    {\n                        string filePath = Path.GetFullPath(Path.Combine(applicationFolder, entry.FullName));\n                        if (!filePath.StartsWith(applicationFolder + Path.DirectorySeparatorChar))\n                            throw new IOException(\"Extracting Zip entry would have resulted in a file outside the specified destination directory.\");\n\n                        if ((entry.Name == \"dnsApp.config\") && File.Exists(filePath))\n                            continue; //avoid overwriting existing config file\n\n                        if ((entry.Length == 0) && (entry.Name.Length == 0) && entry.FullName.EndsWith('/'))\n                        {\n                            //directory entry\n                            Directory.CreateDirectory(filePath);\n                        }\n                        else\n                        {\n                            //file entry\n                            Directory.CreateDirectory(Path.GetDirectoryName(filePath));\n\n                            await entry.ExtractToFileAsync(filePath, true);\n                        }\n                    }\n","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Applications/DnsApplicationManager.cs#L418-L454","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the zip's entry list before install and reject entries containing '..' or rooted paths.","Re-package the app so all entries are relative to the application root with no parent traversal.","Only install apps from trusted authors; verify the zip's directory structure after extraction tooling.","If you control packaging, build with a tool that emits only relative, in-root entries (e.g. zip from inside the folder)."],"exampleFix":"// before\nusing var zip = new ZipArchive(stream, ZipArchiveMode.Read);\nforeach (var e in zip.Entries) { /* trust entries */ }\n\n// after\nusing var zip = new ZipArchive(stream, ZipArchiveMode.Read);\nif (zip.Entries.Any(e => e.FullName.Contains(\"..\") || Path.IsPathRooted(e.FullName)))\n    throw new InvalidOperationException(\"Refusing zip with traversal/rooted entries\");","handlingStrategy":"validation","validationCode":"bool ZipIsSafe(Stream s)\n{\n    using var zip = new ZipArchive(s, ZipArchiveMode.Read, leaveOpen: true);\n    return zip.Entries.All(e => !e.FullName.Contains(\"..\") && !Path.IsPathRooted(e.FullName));\n}","typeGuard":"static bool IsSafeZipEntry(string fullName) =>\n    !fullName.Contains(\"..\") && !Path.IsPathRooted(fullName);","tryCatchPattern":"try { await mgr.InstallApplicationAsync(name, zip); }\ncatch (IOException ex) when (ex.Message.Contains(\"outside the specified destination\")) { log.Warn($\"Rejected traversal zip for {name}\"); }","preventionTips":["Pre-scan zips for '..' or rooted entries before install.","Only install apps from trusted sources.","Build app packages from inside the app root so all entries are relative."],"tags":["security","zip-slip","path-traversal","dns-app"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}