CVE-2025-3445: mholt/archiver Vulnerable to Path Traversal via Crafted ZIP File
Executive Summary
CVE-2025-3445 identifies a critical "Zip Slip" path traversal vulnerability in the mholt/archiver
Go library. This vulnerability allows an attacker to use a crafted ZIP file containing path traversal symlinks to create or overwrite files on the affected system. The vulnerability specifically affects the archiver.Unarchive
function when used with ZIP files such that a malicious ZIP file can be extracted in a way that writes files outside the intended output directory. This could lead to privilege escalation, code execution, and other severe security issues.
Technical Details
Affected Systems and Software Versions
The mholt/archiver
Go library is affected by this vulnerability in versions up to and including 3.5.1. The archiver.Unarchive
function is the main point of vulnerability where a crafted ZIP file can be used to exploit path traversal.
Affected Component:
- Package Name:
github.com/mholt/archiver
- Ecosystem: Go
- Vulnerable Version Range:
<= 3.5.1
- First Patched Version: None (The project has been deprecated and a new project called
mholt/archives
has been created, which removes theUnarchive()
functionality in its initial releasev0.1.0
).
Root Cause Analysis
The root cause of CVE-2025-3445 is a "Zip Slip" vulnerability within the mholt/archiver
library. "Zip Slip" is a well-known vulnerability that occurs when an application extracts files from a ZIP archive without properly sanitizing the file names. This can lead to files being written outside the intended extraction directory, which can be used to overwrite or create files anywhere on the filesystem where the application has write permissions.
The archiver.Unarchive
function is used to extract files from a ZIP archive into a specified output directory. However, if a ZIP file contains a file entry with a path that includes directory traversal characters (e.g., ../../../../etc/passwd
), the archiver.Unarchive
function might write the file outside the intended output directory.
Code Example
To understand the archiver.Unarchive
function, here is a typical usage:
import (
"github.com/mholt/archiver/v3"
)
func main() {
zipFile := "malicious.zip"
outputDir := "output"
err := archiver.Unarchive(zipFile, outputDir)
if err != nil {
log.Fatal(err)
}
}
The archiver.Unarchive
function is defined in the mholt/archiver
library. The vulnerability arises because the function does not properly validate and sanitize the file paths within the ZIP archive.
Detailed Code Analysis
The mholt/archiver
library handles the unarchiving process in the Unarchive
function defined in archiver.go
:
func Unarchive(source, destination string) error {
format, err := FormatFromPath(source)
if err != nil {
return err
}
return format.Open(source).Unarchive(destination)
}
The Unarchive
function calls format.Open(source).Unarchive(destination)
, which in turn calls the Unarchive
method of the specific archive format (e.g., Zip
).
The Zip
struct's Unarchive
method is defined in zip.go
:
func (z *Zip) Unarchive(source, destination string) error {
zr, err := z.Open(source)
if err != nil {
return errors.Wrapf(err, "opening zip archive for unarchiving: %s", source)
}
defer zr.Close()
return zr.Unarchive(destination)
}
The Unarchive
method of Zip
calls zr.Unarchive(destination)
, where zr
is an instance of ZipReadCloser
. The Unarchive
method for ZipReadCloser
iterates through the files in the ZIP archive and extracts them to the destination directory.
The part where the actual extraction happens is in the extractAndWriteFile
function called by Unarchive
:
func (z *Zip) extractAndWriteFile(f *zip.File, destination string) error {
filePath := filepath.Join(destination, f.Name)
if f.FileInfo().IsDir() {
if err := mkdir(filePath); err != nil {
return errors.Wrapf(err, "making directory for file %s", filePath)
}
return nil
}
if err := mkdir(filepath.Dir(filePath)); err != nil {
return errors.Wrapf(err, "making directory for file %s", filePath)
}
rc, err := f.Open()
if err != nil {
return errors.Wrapf(err, "opening file %s from archive", f.Name)
}
defer rc.Close()
return writeNewFile(filePath, rc, f.FileInfo().Mode())
}
The filePath
is constructed by joining the destination
directory with the file name from the ZIP archive (f.Name
). If f.Name
contains a path traversal sequence such as ../../../../etc/passwd
, filepath.Join
might still result in a path that is outside the destination
directory.
The filepath.Join
function itself does not resolve ..
or .
components until after the join operation. Thus, if f.Name
contains ../
sequences, filepath.Join
might still result in a path that traverses outside the destination
directory.
Mitigation Strategies
Immediate Mitigation
-
Avoid using
mholt/archiver
versions<= 3.5.1
for unarchiving untrusted ZIP files. -
Manually validate file paths within the ZIP archive before extraction. For instance, you can check if the resolved path is still within the intended output directory:
import ( "path/filepath" "strings" ) func isPathSafe(filePath, destination string) (bool, error) { dest := filepath.Clean(destination) fullPath := filepath.Join(dest, filePath) relPath, err := filepath.Rel(dest, fullPath) if err != nil { return false, err } if strings.HasPrefix(relPath, ".."+string(filepath.Separator)) { return false, nil } return true, nil }
-
Upgrade to a new library such as
mholt/archives
(if applicable), which has removed theUnarchive
functionality in its initial releasev0.1.0
.
Long-term Mitigation
- Migrate to
mholt/archives
if theUnarchive
functionality is not required. - Implement strict input validation for any file operations, ensuring that file paths are sanitized and validated against a whitelist of allowed characters and patterns.
- Follow security best practices such as running applications with the least privilege necessary to limit the potential impact of a successful exploit.
References
- GitHub Advisory: GHSA-7vpp-9cxj-q8gv
- mholt/archiver GitHub Repository
- mholt/archives GitHub Repository
- CVE-2024-0406 (A similar vulnerability found in TAR files within the same
mholt/archiver
library.)
Conclusion
The CVE-2025-3445 vulnerability in the mholt/archiver
Go library is a critical "Zip Slip" path traversal issue that allows an attacker to use a crafted ZIP file to write files outside the intended output directory. This can lead to severe security consequences such as privilege escalation and code execution. The mholt/archiver
library has been deprecated, and users are advised to migrate to mholt/archives
or implement strict input validation and sanitization for file paths when handling ZIP files.