Portainer CVE-2026-44884
MEDIUMSeverity by source
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
A missing authorization vulnerability in the Custom Template file endpoint (GET /api/custom_templates/{id}/file) allows any authenticated user to read the file content of any custom template by enumerating sequential integer IDs, bypassing Resource Control access restrictions. Template files may contain environment-specific values such as connection strings, API tokens, or registry credentials that administrators would not expect standard users to read.
Severity
Medium
CWE-862 - Missing Authorization
Exploitation requires an authenticated user account and at least one custom template to exist. Template files are returned verbatim and may contain embedded credentials.
Affected Versions
The vulnerability exists in every Portainer release since custom templates were introduced - the customTemplateFile handler has never performed an authorization check.
Fixes are included in the following releases:
| Branch | First vulnerable | Fixed in |
|---|---|---|
| 2.33.x (LTS) | 2.33.0 | 2.33.8 |
| 2.39.x (LTS) | 2.39.0 | 2.39.1 |
Portainer 2.40.0 and later are not affected - the fix was already on develop when the 2.40.x STS line branched. Portainer LTS branches receive fixes for 6 months plus a 3-month overlap after the next LTS ships. 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
There is no runtime configuration that blocks the vulnerable endpoint directly. Administrators who cannot immediately upgrade can reduce exposure by:
- Avoiding storing secrets in custom templates until the patched release is deployed. Move sensitive configuration values to Portainer environment variables or an external secret store.
- Reviewing existing custom templates for embedded secrets. Assume any secret previously stored in a custom template on an unpatched instance has been exposed to every authenticated user and rotate accordingly.
Neither replaces the fix.
Affected Code
The customTemplateFile handler in api/http/handler/customtemplates/customtemplate_file.go (lines 30-53) retrieves a custom template by its numeric ID and returns the file content without performing any authorization check.
All other custom template endpoints properly verify access:
| Endpoint | Method | Authorization Check |
|---|---|---|
/api/custom_templates/{id} | GET (inspect) | userCanEditTemplate() + UserCanAccessResource() |
/api/custom_templates/{id} | PUT (update) | userCanEditTemplate() |
/api/custom_templates/{id} | DELETE | userCanEditTemplate() |
/api/custom_templates | GET (list) | FilterAuthorizedCustomTemplates() |
/api/custom_templates/{id}/file | GET | None |
Vulnerable code (customtemplate_file.go:30-53):
func (handler *Handler) customTemplateFile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
customTemplateID, _ := request.RetrieveNumericRouteVariableValue(r, "id")
customTemplate, _ := handler.DataStore.CustomTemplate().Read(portainer.CustomTemplateID(customTemplateID))
// NO AUTHORIZATION CHECK
fileContent, _ := handler.FileService.GetFileContent(customTemplate.ProjectPath, entryPath)
return response.JSON(w, &fileResponse{FileContent: string(fileContent)})
}Secure reference (customtemplate_inspect.go:50-75):
canEdit := userCanEditTemplate(customTemplate, securityContext)
hasAccess := authorization.UserCanAccessResource(securityContext.UserID, teamIDs, resourceControl)
if !canEdit && !hasAccess {
return httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied)
}Impact
Any authenticated user (including the lowest-privilege standard user) can read the file content of every custom template in the instance. Custom templates commonly contain Docker Compose configuration, which may include environment-specific secrets such as database connection strings, API tokens, or registry credentials.
Timeline
- 2026-02-11: Reported via GitHub Security Advisory by duddnr0615k.
- 2026-03-04: Fix merged to
developand cherry-picked torelease/2.39. - 2026-03-19: 2.39.1 released with fix.
- 2026-03-25: 2.40.0 released with fix already present from branch cut.
- 2026-05-07: 2.33.8 released.
Credit
- duddnr0615k - identified and reported the missing authorization check on the custom template file endpoint.
AnalysisAI
Portainer's custom template file endpoint (GET /api/custom_templates/{id}/file) exposes template file contents to any authenticated user due to a completely absent authorization check in the customTemplateFile handler - a check that every other custom template endpoint correctly implements. Authenticated users at any privilege level can enumerate sequential integer template IDs to read Docker Compose files belonging to templates they have no explicit access to, potentially harvesting embedded secrets such as database connection strings, API tokens, and registry credentials. No public exploit or CISA KEV listing has been identified at time of analysis; however, exploitation requires only a valid session and sequential ID guessing, making it trivially scriptable against any unpatched multi-tenant Portainer instance.
Technical ContextAI
Portainer is a container management platform written in Go (pkg:go/github.com/portainer/portainer) that exposes a REST API for managing Docker and Kubernetes environments. The vulnerable component is the customTemplateFile HTTP handler in api/http/handler/customtemplates/customtemplate_file.go (lines 30-53). Custom templates are stored with sequential integer IDs and may include Docker Compose files containing environment-specific configuration. CWE-862 (Missing Authorization) identifies the root cause: the handler retrieves and returns template file content based solely on a caller-supplied numeric ID without invoking any of Portainer's existing authorization primitives - specifically userCanEditTemplate(), UserCanAccessResource(), or FilterAuthorizedCustomTemplates(). The Resource Control system that governs access to Portainer resources is entirely bypassed for this specific endpoint, while all sibling endpoints (inspect via GET /api/custom_templates/{id}, update via PUT, delete, and list) correctly enforce access controls.
RemediationAI
The primary fix is to upgrade to a patched release: Portainer 2.33.8 for users on the 2.33.x LTS branch (release notes at https://github.com/portainer/portainer/releases/tag/2.33.8) or Portainer 2.39.1 for users on the 2.39.x LTS branch (https://github.com/portainer/portainer/releases/tag/2.39.2); users already on 2.40.0 or later are not affected and require no action. For administrators who cannot immediately upgrade, the vendor offers two compensating controls, neither of which substitutes for patching: first, stop embedding secrets in custom template files and migrate sensitive values to Portainer environment variables or an external secret store - this prevents new credential exposure but does not block enumeration of existing templates; second, audit all existing custom templates for embedded credentials and rotate any secrets that may have been accessible to standard users, treating them as fully compromised given that any authenticated user could have read them. Users on EOL versions prior to 2.33.0 must upgrade to a supported LTS release, as no backport will be issued for those versions.
An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl
runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac
Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post
Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build
Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l
Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c
Vasion Print (formerly PrinterLogic) Virtual Appliance Host prior to version 25.2.169 and Application prior to version 2
An issue in Plone Docker Official Image 5.2.13 (5221) open-source software that could allow for remote code execution du
Tandoor Recipes is an application for managing recipes, planning meals, and building shopping lists. Rated critical seve
Unauthenticated remote code execution in 9router (npm package) versions 0.4.30 through 0.4.36 allows network-adjacent at
Same weakness CWE-862 – Missing Authorization
View allSame technique Authentication Bypass
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-cqpq-2fgr-8mvc