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 the Unarchive() functionality in its initial release v0.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

  1. Avoid using mholt/archiver versions <= 3.5.1 for unarchiving untrusted ZIP files.

  2. 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
    }
    
  3. Upgrade to a new library such as mholt/archives (if applicable), which has removed the Unarchive functionality in its initial release v0.1.0.

Long-term Mitigation

  1. Migrate to mholt/archives if the Unarchive functionality is not required.
  2. Implement strict input validation for any file operations, ensuring that file paths are sanitized and validated against a whitelist of allowed characters and patterns.
  3. Follow security best practices such as running applications with the least privilege necessary to limit the potential impact of a successful exploit.

References

  1. GitHub Advisory: GHSA-7vpp-9cxj-q8gv
  2. mholt/archiver GitHub Repository
  3. mholt/archives GitHub Repository
  4. 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.

Read more