# Portainer missing authorization on Docker plugin endpoints, which allows host RCE

Portainer's Docker API proxy allowlist omitted /plugins, so non-admin endpoint users could pull and enable root-privileged Docker plugins for host RCE.

- Payout: Research
- Severity: critical
- Program: Portainer
- Found by: ikkebr
- Disclosed: 2026-05-14
- CVE: CVE-2026-44848
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-rrmm-9v76-h3p4
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-rrmm-9v76-h3p4
- Tags: area:cloud domain:cloud domain:infra impact:privesc impact:rce kind:research platform:ghsa sev:critical tech:docker tech:go vuln:access-control vuln:auth-bypass vuln:privilege-escalation vuln:rce

**Target:** Docker API proxy in Portainer (api/http/proxy/factory/docker/transport.go)
**Root cause:** The proxy routes requests by first path segment through prefixProxyFuncMap, which applies RBAC; /plugins was never registered, so plugin requests fall through to executeDockerRequest and reach the Docker daemon with no authorization check.
**Steps:**
1. Authenticate as a non-admin Portainer user with RBAC access to a Docker endpoint.
2. `POST /plugins/pull` to pull an arbitrary plugin from any registry.
3. Grant the plugin its requested privileges, including `CAP_SYS_ADMIN` and host-path mounts.
4. `POST /plugins/{name}/enable` to enable the plugin, which Docker runs as root on the host.
**How found:** Reporter examined the proxy's prefix-allowlist routing model and noticed /plugins was absent from prefixProxyFuncMap, meaning the fall-through path forwarded requests without authorization.
**Exploitation:** A standard user with endpoint access pulls a crafted plugin, accepts its privileged config (CAP_SYS_ADMIN, host mounts), and enables it; Docker executes the plugin as root on the host, giving filesystem access and root-equivalent control.
**Escalation:** Low-privileged Portainer user escalates to root on the Docker host via plugin privileges.
**Insight:** When a proxy authorizes by allowlisting path prefixes, enumerate the full Docker API surface and diff it against the allowlist — any unlisted prefix (plugins, session, swarm/unlock-key, etc.) silently bypasses RBAC via the fall-through branch.

