1+ <!DOCTYPE qhelp PUBLIC
2+ "-//Semmle//qhelp//EN"
3+ "qhelp.dtd">
4+ <qhelp >
5+ <overview >
6+ <p >Extracting files from a malicious zip file, or similar type of archive,
7+ is at risk of directory traversal attacks if filenames from the archive are
8+ not properly validated.</p >
9+
10+ <p >Zip archives contain archive entries representing each file in the archive. These entries
11+ include a file path for the entry, but these file paths are not restricted and may contain
12+ unexpected special elements such as the directory traversal element (<code >..</code >). If these
13+ file paths are used to create a filesystem path, then a file operation may happen in an
14+ unexpected location. This can result in sensitive information being
15+ revealed or deleted, or an attacker being able to influence behavior by modifying unexpected
16+ files.</p >
17+
18+ <p >For example, if a zip file contains a file entry <code >..\sneaky-file</code >, and the zip file
19+ is extracted to the directory <code >c:\output</code >, then naively combining the paths would result
20+ in an output file path of <code >c:\output\..\sneaky-file</code >, which would cause the file to be
21+ written to <code >c:\sneaky-file</code >.</p >
22+
23+ </overview >
24+ <recommendation >
25+
26+ <p >Ensure that output paths constructed from zip archive entries are validated to prevent writing
27+ files to unexpected locations.</p >
28+
29+ <p >The recommended way of writing an output file from a zip archive entry is to conduct the following in sequence:</p >
30+
31+ <ol >
32+ <li >Use <code >Path.Combine(destinationDirectory, archiveEntry.FullName)</code > to determine the raw
33+ output path.</li >
34+ <li >Use <code >Path.GetFullPath(..)</code > on the raw output path to resolve any directory traversal
35+ elements.</li >
36+ <li >Use <code >Path.GetFullPath(destinationDirectory + Path.DirectorySeparatorChar)</code > to
37+ determine the fully resolved path of the destination directory.</li >
38+ <li >Validate that the resolved output path <code >StartsWith</code > the resolved destination
39+ directory, aborting if this is not true.</li >
40+ </ol >
41+
42+ <p >Another alternative is to validate archive entries against a whitelist of expected files.</p >
43+
44+ </recommendation >
45+ <example >
46+
47+ <p >In this example, a file path taken from a zip archive item entry is combined with a
48+ destination directory. The result is used as the destination file path without verifying that
49+ the result is within the destination directory. If provided with a zip file containing an archive
50+ path like <code >..\sneaky-file</code >, then this file would be written outside the destination
51+ directory.</p >
52+
53+ <sample src =" examples/ZipSlipBad.ps1" />
54+
55+ <p >To fix this vulnerability, we can instead use the PowerShell command <code >Expand-Archive</code >
56+ which is safe against this vulnerability by default starting from PowerShell 5.0.</p >
57+
58+ <sample src =" examples/ZipSlipGood1.ps1" />
59+
60+ <p >If you need to use the lower-level functionality offered by <code >System.IO.Compression.ZipFile</code >
61+ we need to make three changes. Firstly, we need to resolve any directory traversal or other special
62+ characters in the path by using <code >Path.GetFullPath</code >. Secondly, we need to identify the
63+ destination output directory, again using <code >Path.GetFullPath</code >, this time on the output directory.
64+ Finally, we need to ensure that the resolved output starts with the resolved destination directory, and
65+ throw an exception if this is not the case.</p >
66+
67+ <sample src =" examples/ZipSlipGood2.ps1" />
68+
69+ </example >
70+ <references >
71+
72+ <li >
73+ Snyk:
74+ <a href =" https://snyk.io/research/zip-slip-vulnerability" >Zip Slip Vulnerability</a >.
75+ </li >
76+ <li >
77+ OWASP:
78+ <a href =" https://owasp.org/www-community/attacks/Path_Traversal" >Path Traversal</a >.
79+ </li >
80+
81+ </references >
82+ </qhelp >
0 commit comments