VYPR
High severityNVD Advisory· Published Aug 1, 2025· Updated Aug 4, 2025

Traefik's Client Plugin is Vulnerable to Path Traversal, Arbitrary File Overwrites and Remote Code Execution

CVE-2025-54386

Description

Traefik is an HTTP reverse proxy and load balancer. In versions 2.11.27 and below, 3.0.0 through 3.4.4 and 3.5.0-rc1, a path traversal vulnerability was discovered in WASM Traefik’s plugin installation mechanism. By supplying a maliciously crafted ZIP archive containing file paths with ../ sequences, an attacker can overwrite arbitrary files on the system outside of the intended plugin directory. This can lead to remote code execution (RCE), privilege escalation, persistence, or denial of service. This is fixed in versions 2.11.28, 3.4.5 and 3.5.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/traefik/traefik/v2Go
< 2.11.282.11.28
github.com/traefik/traefik/v3Go
< 3.4.53.4.5
github.com/traefik/traefik/v3Go
>= 3.5.0-rc1, < 3.5.03.5.0

Affected products

1

Patches

1
5ef853a0c530

Fix client arbitrary file access during archive extraction zipslip

https://github.com/traefik/traefikZeroday BYTEJul 22, 2025via ghsa
1 file changed · +32 6
  • pkg/plugins/client.go+32 6 modified
    @@ -240,6 +240,8 @@ func (c *Client) Unzip(pName, pVersion string) error {
     		return nil
     	}
     
    +	// Unzip as a generic archive if the module unzip fails.
    +	// This is useful for plugins that have vendor directories or other structures.
     	return c.unzipArchive(pName, pVersion)
     }
     
    @@ -280,24 +282,48 @@ func unzipFile(f *zipa.File, dest string) error {
     
     	defer func() { _ = rc.Close() }()
     
    +	// Split to discard the first part of the path, which is [organization]-[project]-[release commit sha1] when the archive is a Yaegi go plugin with vendoring.
     	pathParts := strings.SplitN(f.Name, "/", 2)
    -	p := filepath.Join(dest, pathParts[1])
    +	if len(pathParts) < 2 {
    +		return fmt.Errorf("no root directory: %s", f.Name)
    +	}
    +
    +	// Validate and sanitize the file path.
    +	cleanName := filepath.Clean(pathParts[1])
    +	if strings.Contains(cleanName, "..") {
    +		return fmt.Errorf("invalid file path in archive: %s", f.Name)
    +	}
    +
    +	filePath := filepath.Join(dest, cleanName)
    +	absFilePath, err := filepath.Abs(filePath)
    +	if err != nil {
    +		return fmt.Errorf("resolving file path: %w", err)
    +	}
    +
    +	absDest, err := filepath.Abs(dest)
    +	if err != nil {
    +		return fmt.Errorf("resolving destination directory: %w", err)
    +	}
    +
    +	if !strings.HasPrefix(absFilePath, absDest) {
    +		return fmt.Errorf("file path escapes destination directory: %s", absFilePath)
    +	}
     
     	if f.FileInfo().IsDir() {
    -		err = os.MkdirAll(p, f.Mode())
    +		err = os.MkdirAll(filePath, f.Mode())
     		if err != nil {
    -			return fmt.Errorf("unable to create archive directory %s: %w", p, err)
    +			return fmt.Errorf("unable to create archive directory %s: %w", filePath, err)
     		}
     
     		return nil
     	}
     
    -	err = os.MkdirAll(filepath.Dir(p), 0o750)
    +	err = os.MkdirAll(filepath.Dir(filePath), 0o750)
     	if err != nil {
    -		return fmt.Errorf("unable to create archive directory %s for file %s: %w", filepath.Dir(p), p, err)
    +		return fmt.Errorf("unable to create archive directory %s for file %s: %w", filepath.Dir(filePath), filePath, err)
     	}
     
    -	elt, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
    +	elt, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
     	if err != nil {
     		return err
     	}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.