解压缩包含符号链接的归档文件导致任意文件写入¶
ID: go/unsafe-unzip-symlink
Kind: path-problem
Security severity: 7.5
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-022
Query suites:
- go-code-scanning.qls
- go-security-extended.qls
- go-security-and-quality.qls
从恶意 zip 归档文件中解压缩符号链接时,如果不验证目标文件路径是否在目标目录内,则可能会覆盖目标目录之外的文件。如果归档路径中存在先前解压缩的符号链接或目录遍历元素和链接(..
),则可能会发生这种情况。
此问题与 go/zipslip
查询检测到的 ZipSlip 漏洞相关;有关恶意归档文件漏洞的更多常规信息,请参阅该查询的帮助。此查询考虑了从归档文件中解压缩符号链接的特定情况,在这种情况下,解压缩代码在检查它是否即将解压缩指向目标解压缩目录之外的链接时,必须注意现有的符号链接。
建议¶
确保验证从 zip 归档条目构造的输出路径。这包括解析任何先前解压缩的符号链接(例如,使用 path/filepath.EvalSymlinks
),以防止将文件或链接写入意外的位置。
示例¶
在此示例中,使用语法 filepath.Rel
函数从归档文件中解压缩链接,以检查链接及其目标是否在目标目录内。但是,解压缩代码不会解析先前解压缩的链接,因此,一对链接(如 subdir/parent -> ..
,后跟 escape -> subdir/parent/.. -> subdir/../..
)会留下一个指向归档根目录的父目录的链接。语法 Rel
无效,因为它将 subdir/parent/..
等同于 subdir/
,但在 subdir/parent
是符号链接时,情况并非如此。
package main
import (
"archive/tar"
"io"
"os"
"path/filepath"
"strings"
)
func isRel(candidate, target string) bool {
// BAD: purely syntactic means are used to check
// that `candidate` does not escape from `target`
if filepath.IsAbs(candidate) {
return false
}
relpath, err := filepath.Rel(target, filepath.Join(target, candidate))
return err == nil && !strings.HasPrefix(filepath.Clean(relpath), "..")
}
func unzipSymlink(f io.Reader, target string) {
r := tar.NewReader(f)
for {
header, err := r.Next()
if err != nil {
break
}
if isRel(header.Linkname, target) && isRel(header.Name, target) {
os.Symlink(header.Linkname, header.Name)
}
}
}
要修复此漏洞,请在检查链接的目标是否可接受之前,解析预先存在的符号链接
package main
func isRel(candidate, target string) bool {
// GOOD: resolves all symbolic links before checking
// that `candidate` does not escape from `target`
if filepath.IsAbs(candidate) {
return false
}
realpath, err := filepath.EvalSymlinks(filepath.Join(target, candidate))
if err != nil {
return false
}
relpath, err := filepath.Rel(target, realpath)
return err == nil && !strings.HasPrefix(filepath.Clean(relpath), "..")
}
参考¶
Snyk:Zip Slip 漏洞。
OWASP:路径遍历。
常见弱点枚举:CWE-22。