Portainer CVE-2026-44885
MEDIUMSeverity by source
AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:L
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:L
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
Portainer's backup restore feature accepts a .tar.gz archive and extracts it to a target directory on the server. The extraction function (ExtractTarGz in api/archive/targz.go) constructed output paths using filepath.Clean(filepath.Join(outputDirPath, header.Name)). This combination does not prevent directory traversal - a tar entry named ../../etc/cron.d/evil resolves to a path outside the extraction root, so a crafted archive can write files to arbitrary locations on the server filesystem.
Severity
Medium
CWE-22 - Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Exploitation requires administrator access to Portainer's backup restore endpoint. An administrator who is deceived into restoring a malicious archive, or whose credentials are compromised, can use this path to write files outside the Portainer data directory.
Affected Versions
The vulnerability exists in every Portainer release prior to 2.39.0 - ExtractTarGz has used filepath.Clean(filepath.Join()) since it was introduced. The fix shipped with 2.39.0 (patched on develop before the 2.39 branch cut); 2.34.x-2.38.x STS releases are also affected but are end-of-life and will not receive a fix.
| Branch | First vulnerable | Fixed in |
|---|---|---|
| 2.33.x (LTS) | 2.33.0 | 2.33.8 |
Portainer 2.39.0 and later are not affected - the fix was present from the initial 2.39.0 release. All releases prior to 2.33.0 are end-of-life and will not receive a fix; users on EOL versions should upgrade to a supported release.
Workarounds
Administrators who cannot immediately upgrade should:
- Only restore archives from trusted sources. Do not restore archives received from untrusted parties or transmitted over unencrypted channels.
- Use backup encryption. Portainer's optional backup encryption requires the correct passphrase to decrypt before extraction; an attacker without the passphrase cannot craft a valid encrypted archive.
Neither of these replaces the fix.
Affected Code
ExtractTarGz in api/archive/targz.go constructed output paths without safe containment:
// api/archive/targz.go (pre-fix)
case tar.TypeReg:
p := filepath.Clean(filepath.Join(outputDirPath, header.Name))filepath.Join resolves ../ components lexically and filepath.Clean normalises the result, but neither verifies the final path remains inside outputDirPath. The fix replaces this with filesystem.JoinPaths, which forces all path components to be relative to the trusted root:
// api/archive/targz.go (post-fix)
case tar.TypeReg:
p := filesystem.JoinPaths(outputDirPath, header.Name)Impact
- Arbitrary file write at any path accessible to the Portainer process (typically root in containerised deployments), overriding filesystem boundaries of the data directory.
- Potential host persistence by writing to cron directories, SSH authorised key files, or executable paths, depending on how the container is configured and what host paths are accessible.
The practical severity is reduced because exploitation requires administrative privileges within Portainer.
Timeline
- 2026-02-16: Fix merged to develop (#1875).
- 2026-02-25: 2.39.0 released with fix.
- 2026-05-07: 2.33.8 released with backport fix.
Credits
Reported by Kolega.
AnalysisAI
Path traversal in Portainer's backup restore feature allows arbitrary file write to the server filesystem via crafted tar.gz archives. Versions prior to 2.39.0 (and 2.33.8 in the LTS branch) use unsafe path joining in the ExtractTarGz function, permitting entries like ../../etc/cron.d/evil to escape the extraction root. An authenticated administrator who restores a malicious archive or whose credentials are compromised can write files to any path accessible to the Portainer process, potentially establishing persistence on the host. Exploitation requires administrative privileges within Portainer, reducing practical impact.
Technical ContextAI
The vulnerability stems from CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) in the tar archive extraction logic. Portainer's ExtractTarGz function in api/archive/targz.go used the Go standard library functions filepath.Clean(filepath.Join(outputDirPath, header.Name)) to construct output paths from tar headers. While filepath.Join resolves relative path components and filepath.Clean normalizes the result, neither function validates that the final path remains within the intended extraction root directory. This permits directory traversal sequences (../) in tar entry names to resolve outside the trusted extraction boundary. The fix replaces the unsafe path construction with filesystem.JoinPaths, a custom function that forces all path components to be relative to the trusted root, preventing escape. The vulnerability has existed since ExtractTarGz was introduced and affects all Portainer releases before the fix was merged to develop on 2026-02-16.
RemediationAI
Upgrade immediately to Portainer 2.33.8 (for LTS users) or 2.39.0 or later (for general release users). The patch replaces the unsafe filepath.Clean(filepath.Join()) construction with filesystem.JoinPaths, which enforces path containment and is confirmed in commit e02ae6b2fb69668d1cd7d07cb873dfcdd9cf1e42. Organizations unable to patch immediately should implement the following compensating controls: (1) Only restore backup archives from trusted, verified sources and restrict administrator access to the restore endpoint to authorized personnel only (trade-off: reduces convenience and team flexibility). (2) Enable Portainer's optional backup encryption feature with a strong passphrase; this prevents crafting valid malicious archives without the correct encryption key (trade-off: requires managing encryption keys and adds operational complexity). (3) Run Portainer in a restricted container with reduced host filesystem mount permissions and minimal UID/GID privileges to limit the scope of file-write impact (trade-off: may break legitimate functionality if Portainer requires broad host access). (4) Monitor the Portainer backup restore endpoint with audit logging and alerting to detect suspicious restore activities, particularly restores of unusually large or externally-sourced archives (trade-off: reactive rather than preventive). None of these workarounds eliminates the vulnerability; patching is the permanent fix. Refer to GitHub advisory GHSA-m8fg-67j7-cx4v for additional details.
Same weakness CWE-22 – Path Traversal
View allSame technique Path Traversal
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-m8fg-67j7-cx4v