mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
C#: ZipSlip - Add qhelp file.
This adds a help file which describes the problem, provides recommendations on how to fix it and an example.
This commit is contained in:
77
csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp
Normal file
77
csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp
Normal file
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Extracting files from a malicious zip archive without validating that the destination file path
|
||||
is within the destination directory can cause files outside the destination directory to be
|
||||
overwritten, due to the possible presence of directory traversal elements ("..") in archive
|
||||
paths.</p>
|
||||
|
||||
<p>Zip archives contain archive entries representing each file in the archive. These entries
|
||||
include a file path for the entry, but these file paths are not restricted and may contain
|
||||
unexpected special elements such as the directory traversal element (".."). If these file paths are
|
||||
use to determine an output file to write the contents of the archive item to, then the file may be
|
||||
written to an unexpected location. This can result in sensitive information being revealed or
|
||||
deleted, or an attacker being able to influence behavior by modifying unexpected files.</p>
|
||||
|
||||
<p>For example, if a zip file contains a file entry <code>..\sneaky-file</code>, and the zip file
|
||||
is extracted to the directory <code>c:\output</code>, then naively combining the paths would result
|
||||
in an output file path of <code>c:\output\..\sneaky-file</code>, which would cause the file to be
|
||||
written to <code>c:\sneaky-file</code>.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Ensure that output paths constructed from zip archive entries are validated to prevent writing
|
||||
files to unexpected locations.</p>
|
||||
|
||||
<p>The recommended way of writing an output file from a zip archive entry is to:</p>
|
||||
|
||||
<ol>
|
||||
<li>Use <code>Path.Combine(destinationDirectory, archiveEntry.FullName)</code> to determine the raw
|
||||
output path.</li>
|
||||
<li>Use <code>Path.GetFullPath(..)</code> on the raw output path to resolve any directory traversal
|
||||
elements.</li>
|
||||
<li>Use <code>Path.GetFullPath(destinationDirectory + Path.DirectorySeparatorChar)</code> to
|
||||
determine the fully resolved path of the destination directory.</li>
|
||||
<li>Validate that the resolved output path <code>StartsWith</code> the resolved destination
|
||||
directory, aborting if this is not true.</li>
|
||||
</ol>
|
||||
|
||||
<p>Another alternative is to validate archive entries against a whitelist of expected files.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this example, a file path taken from a zip archive item entry is combined with a
|
||||
destination directory, and the result is used as the destination file path without verifying that
|
||||
the result is within the destination directory. If provided with a zip file containing an archive
|
||||
path like <code>..\sneaky-file</code>, then this file would be written outside of the destination
|
||||
directory.</p>
|
||||
|
||||
<sample src="ZipSlipBroken.cs" />
|
||||
|
||||
<p>In order to fix this vulnerability, we need to make three changes. Firstly, we need to resolve any
|
||||
directory traversal or other special characters in the path by using <code>Path.GetFullPath</code>.
|
||||
Secondly, we need to identify the destination output directory, again using
|
||||
<code>Path.GetFullPath</code>, this time on the output directory. Finally, we need to ensure that
|
||||
the resolved output starts with the resolved destination directory, and throw and exception if this
|
||||
is not the case.</p>
|
||||
|
||||
<sample src="ZipSlipFixed.cs" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Snyk:
|
||||
<a href="https://snyk.io/research/zip-slip-vulnerability">Zip Slip Vulnerability</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Path_traversal">Path Traversal</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
7
csharp/ql/src/Security Features/CWE-022/ZipSlipBroken.cs
Normal file
7
csharp/ql/src/Security Features/CWE-022/ZipSlipBroken.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
public static void WriteToDirectory(IArchiveEntry entry,
|
||||
string destDirectory,
|
||||
ExtractionOptions options){
|
||||
string file = Path.GetFileName(entry.Key);
|
||||
string destFileName = Path.Combine(destDirectory, file);
|
||||
entry.WriteToFile(destFileName, options);
|
||||
}
|
||||
11
csharp/ql/src/Security Features/CWE-022/ZipSlipFixed.cs
Normal file
11
csharp/ql/src/Security Features/CWE-022/ZipSlipFixed.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
public static void WriteToDirectory(IArchiveEntry entry,
|
||||
string destDirectory,
|
||||
ExtractionOptions options){
|
||||
string file = Path.GetFileName(entry.Key);
|
||||
string destFileName = Path.GetFullPath(Path.Combine(destDirecory, entry.Key));
|
||||
string fullDestDirPath = Path.GetFullPath(destDirectory + Path.DirectorySeparatorChar);
|
||||
if (!destFileName.StartsWith(fullDestDirPath)) {
|
||||
throw new ExtractionException("Entry is outside of the target dir: " + destFileName);
|
||||
}
|
||||
entry.WriteToFile(destFileName, options);
|
||||
}
|
||||
Reference in New Issue
Block a user