归档提取期间的任意文件访问(“Zip Slip”)¶
ID: cs/zipslip
Kind: path-problem
Security severity: 7.5
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-022
Query suites:
- csharp-code-scanning.qls
- csharp-security-extended.qls
- csharp-security-and-quality.qls
如果未正确验证来自归档文件的的文件名,则从恶意 zip 文件或类似类型的归档文件中提取文件时,可能会面临目录遍历攻击的风险。
Zip 归档文件包含表示归档文件中每个文件的归档条目。这些条目包含条目的文件路径,但这些文件路径不受限制,并且可能包含意外的特殊元素,例如目录遍历元素(..
)。如果使用这些文件路径来创建文件系统路径,则文件操作可能会在意外的位置发生。这可能会导致敏感信息泄露或被删除,或者攻击者能够通过修改意外的文件来影响行为。
例如,如果 zip 文件包含文件条目 ..\sneaky-file
,并且 zip 文件被提取到目录 c:\output
,则简单地组合路径会导致输出文件路径为 c:\output\..\sneaky-file
,这将导致文件被写入到 c:\sneaky-file
。
建议¶
确保验证从 zip 归档条目构造的输出路径,以防止将文件写入意外的位置。
从 zip 归档条目写入输出文件的推荐方法是
使用
Path.Combine(destinationDirectory, archiveEntry.FullName)
确定原始输出路径。在原始输出路径上使用
Path.GetFullPath(..)
来解析任何目录遍历元素。使用
Path.GetFullPath(destinationDirectory + Path.DirectorySeparatorChar)
确定目标目录的完全解析路径。验证解析后的输出路径是否以
StartsWith
开头,如果不是,则中止。另一种方法是根据预期文件的白名单验证归档条目。
示例¶
在本例中,从 zip 归档项目条目获取的文件路径与目标目录组合在一起。结果用作目标文件路径,而不验证结果是否在目标目录内。如果提供包含 ..\sneaky-file
之类的归档路径的 zip 文件,则此文件将被写入目标目录之外。
using System.IO;
using System.IO.Compression;
class Bad
{
public static void WriteToDirectory(ZipArchiveEntry entry,
string destDirectory)
{
string destFileName = Path.Combine(destDirectory, entry.FullName);
entry.ExtractToFile(destFileName);
}
}
要修复此漏洞,我们需要进行三处更改。首先,我们需要使用 Path.GetFullPath
解析路径中的任何目录遍历或其他特殊字符。其次,我们需要使用 Path.GetFullPath
标识目标输出目录,这次是在输出目录上。最后,我们需要确保解析后的输出以解析后的目标目录开头,如果不是这种情况,则抛出异常。
using System.IO;
using System.IO.Compression;
class Good
{
public static void WriteToDirectory(ZipArchiveEntry entry,
string destDirectory)
{
string destFileName = Path.GetFullPath(Path.Combine(destDirectory, entry.FullName));
string fullDestDirPath = Path.GetFullPath(destDirectory + Path.DirectorySeparatorChar);
if (!destFileName.StartsWith(fullDestDirPath)) {
throw new System.InvalidOperationException("Entry is outside the target dir: " +
destFileName);
}
entry.ExtractToFile(destFileName);
}
}
参考文献¶
Snyk:Zip Slip 漏洞。
OWASP:路径遍历。
常见弱点枚举:CWE-22。