GHSA-rrmm-9v76-h3p4
HIGHGHSA-rrmm-9v76-h3p4 is a high-severity (CVSS 8.8) CWE-862 vulnerability in github.com/portainer/portainer. O3 Security confirms whether GHSA-rrmm-9v76-h3p4 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
Portainer missing authorization on Docker plugin endpoints, which allows host RCE
Real-World Exposure
github.com/portainer/portainer🐹github.com/portainer/portainer🐹github.com/portainer/portainerReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Summary
Portainer enforces Role-Based Access Control (RBAC) on top of the Docker API. The proxy layer routes incoming Docker API requests to per-resource handlers (containers, images, services, volumes, etc.) that apply authorization checks.
The Docker plugin management endpoints (/plugins/*) were not registered with a handler, so standard users with endpoint access could call privileged plugin operations — including installing and enabling plugins — directly against the underlying Docker daemon.
The vulnerability is exposed when a non-admin Portainer user (Standard User role, or any role granted endpoint-level access) has been given access to a Docker endpoint via Portainer RBAC. Administrators and users without Docker endpoint access are not affected.
A regular user with access to a Docker endpoint can:
- Pull an arbitrary plugin from any registry via
POST /plugins/pull. - Grant it the privileges it requests, including
CAP_SYS_ADMINand host-path mounts. - Enable the plugin via
POST /plugins/{name}/enable, at which point Docker runs the plugin with root privileges on the host.
Docker plugins execute as root on the host and can request arbitrary host capabilities and mounts. Enabling a crafted plugin gives the user access to the host filesystem and equivalent to root on the Docker host.
Severity
Critical — CVSS 9.4
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
CWE-862 — Missing Authorization
Affected Versions
The vulnerability exists in every Portainer release where the Docker API proxy uses the prefix-allowlist routing model — /plugins has never been in the allowlist, and the fall-through path has never applied authorization.
Fixes are included in the next release of each supported branch:
| Branch | First vulnerable | Fixed in |
|---|---|---|
| 2.33.x (LTS) | 2.33.0 | 2.33.8 |
| 2.39.x (LTS) | 2.39.0 | 2.39.2 |
| 2.40.x (STS) | 2.40.0 | 2.41.0 |
Portainer LTS branches receive fixes for 6 months plus a 3-month overlap after the next LTS ships. STS releases are supported only until the next STS ships — the 2.40.x STS line ends with the 2.41.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 LTS branch.
Workarounds
Administrators who cannot immediately upgrade can reduce exposure by temporarily revoking Docker endpoint access for non-admin users via Portainer RBAC until the patched release is deployed. This eliminates the attack surface without disruption for administrators. This does not replace the fix.
Affected Code
// api/http/proxy/factory/docker/transport.go (pre-fix)
var prefixProxyFuncMap = map[string]func(...){
"build": ...,
"configs": ...,
"containers": ...,
"images": ...,
"networks": ...,
"nodes": ...,
"secrets": ...,
"services": ...,
"swarm": ...,
"tasks": ...,
"v2": ...,
"volumes": ...,
}
func (transport *Transport) ProxyDockerRequest(request *http.Request) (*http.Response, error) {
// ...
prefix := strings.Split(strings.TrimPrefix(unversionedPath, "/"), "/")[0]
if proxyFunc := prefixProxyFuncMap[prefix]; proxyFunc != nil {
return proxyFunc(transport, request, unversionedPath) // authorized
}
return transport.executeDockerRequest(request) // forwarded without authorization
}
/plugins is not in prefixProxyFuncMap, so requests to plugin endpoints fall through to executeDockerRequest and are forwarded to the Docker daemon without any Portainer-side authorization check.
Impact
An authenticated, non-admin Portainer user with access to any Docker-enabled endpoint can:
- Install and enable arbitrary Docker plugins from any registry.
- Execute plugin code with root privileges on the Docker host (including declaring
CAP_SYS_ADMINand host-path mounts). - Read and modify files on the host filesystem from a restricted account, overriding the administrator's security policy.
Timeline
- 2026-03-16: Reported via GitHub Security Advisory by ikkebr.
- 2026-04-20: Fix merged to
develop,release/2.39, andrelease/2.33. - 2026-04-29: 2.41.0 released.
- 2026-05-07: 2.39.2-LTS and 2.33.8-LTS released.
Credit
- ikkebr — identified and reported the proxy allowlist bypass affecting the Docker plugin management endpoints.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/portainer/portainer | ≥ 2.33.0&&< 2.33.8 | 2.33.8 |
| 🐹Go | github.com/portainer/portainer | ≥ 2.39.0&&< 2.39.2 | 2.39.2 |
| 🐹Go | github.com/portainer/portainer | ≥ 2.40.0&&< 2.41.0 | 2.41.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/portainer/portainer. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update github.com/portainer/portainer to 2.33.8 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-rrmm-9v76-h3p4 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-rrmm-9v76-h3p4 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-rrmm-9v76-h3p4. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-rrmm-9v76-h3p4 in your dependencies?
O3 detects GHSA-rrmm-9v76-h3p4 across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.