Aug 28, 2026·7 min read·54 visits
Uninitialized Portainer CE instances expose administrative configuration and restore endpoints without authentication during the initial five-minute setup window. Remote attackers can exploit this to register admin accounts or restore malicious configurations, leading to complete instance compromise.
An improper authentication vulnerability (CWE-287) in Portainer Community Edition (CE) allows unauthenticated remote attackers to achieve full administrative takeover. During the initial five-minute uninitialized setup window, sensitive endpoints responsible for creating the initial administrator user and restoring database state are publicly accessible without authentication. Attackers can exploit this to create administrative credentials or overwrite the system state with a malicious database configuration.
Portainer Community Edition is an open-source management platform designed to simplify the administration of Docker, Kubernetes, and Nomad environments. The platform provides a centralized web dashboard to deploy applications, manage system resources, and configure container clusters. When first deployed, Portainer launches in an uninitialized state, waiting for the administrator to establish credentials or restore a prior backup configuration. To limit exposure, Portainer implements a default shutdown mechanism that terminates the server if initialization does not occur within five minutes of launch.
During this initial setup state, the application exposes endpoints that receive configuration instructions. Specifically, the endpoints /api/users/admin/init and /api/restore are exposed to allow the platform operator to complete initialization. The vulnerability exists because these endpoints lack authentication controls to verify the initiator's identity. Any network entity capable of reaching the Portainer service during its initial five-minute window can access these endpoints.
This behavior exposes a severe attack surface. Security tools and network scanners regularly scan the internet and internal corporate subnets for uninitialized administrative interfaces. If an attacker discovers an uninitialized Portainer instance before the legitimate administrator completes the setup or before the five-minute timeout expires, they can hijack the initialization process. The security impact is significant because controlling a Portainer instance grants complete execution control over the underlying container runtime and the associated hosting infrastructure.
The underlying technical flaw stems from improper configuration of access controls on endpoints critical to the platform's initialization state. In vulnerable versions of Portainer, the /api/users/admin/init and /api/restore backend routes are registered utilizing the bouncer.PublicAccess middleware. The developers designed these endpoints to run without standard session or token checks because, at startup, no valid users or administrators exist in the database to authenticate against.
The system lacked a secure mechanism to prove out-of-band ownership of the container environment. The application assumed that physical access to the network port on which the instance was deployed equated to administrative authorization. This assumption fails in multi-tenant cloud environments, shared developer networks, or instances inadvertently exposed to the public internet.
While the five-minute setup window restricts the lifetime of the uninitialized state, it does not prevent automated, high-speed scanning systems from exploiting the flaw. A script monitoring target ports can initiate setup payloads milliseconds after the service starts. The vulnerability is classified under CWE-287 (Improper Authentication) due to this failure to confirm the client's authority before executing administrative setup tasks.
To address the authentication gap, Portainer developers introduced a cryptographic validation mechanism known as the Setup Token. During container startup, if the database has no registered administrator accounts, the server executes resolveSetupToken(). This function generates a cryptographically secure 64-character hex-encoded string derived from 32 random bytes.
// setuptoken.go
func Generate() (string, error) {
b := make([]byte, tokenByteLength)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}This generated token is printed directly to the standard output (stdout) container logs. It serves as an out-of-band proof of host access, ensuring only operators with access to the container management infrastructure can retrieve it. The backend handler functions then use constant-time comparison to validate this token prior to processing initialization requests.
// Validate verifies the token from the X-Setup-Token header
func Validate(r *http.Request, expected string) *httperror.HandlerError {
if expected == "" {
return nil
}
provided := r.Header.Get(HeaderName)
if subtle.ConstantTimeCompare([]byte(provided), []byte(expected)) != 1 {
return httperror.Forbidden("Invalid or missing setup token. Provide the X-Setup-Token header.", errInvalidSetupToken)
}
return nil
}The implementation uses subtle.ConstantTimeCompare to defend against timing side-channel attacks, ensuring that attackers cannot brute-force the setup token byte-by-byte based on the server's response time. Once an administrator is configured on subsequent runs, the resolveSetupToken routine detects the existing accounts, registers an empty expected token, and disables the gate. This fix is complete and robust because it anchors setup authorization to container execution-level access.
An attack is structured around two distinct vectors depending on the threat actor's objective: primary account registration or complete database state restoration.
In the first scenario, the attacker targets the /api/users/admin/init endpoint. By sending a standard HTTP POST request containing a fresh JSON payload, the attacker populates the empty database with custom administrative credentials. The server responds with status code 200 OK and generates a JWT authorization token, giving the attacker immediate control over the platform.
POST /api/users/admin/init HTTP/1.1
Host: target-portainer:9000
Content-Type: application/json
Connection: close
{
"Username": "attacker_admin",
"Password": "ComplexPassword_123"
}In the second scenario, the attacker targets the /api/restore endpoint to inject a preconfigured BoltDB state. This state can include defined target endpoints, embedded SSH authorization configurations, or pre-hashed administrative accounts. The attacker uploads a compressed archive containing this malicious database, which Portainer restores instantly.
POST /api/restore HTTP/1.1
Host: target-portainer:9000
Content-Type: multipart/form-data; boundary=---------------------------987654321
Connection: close
-----------------------------987654321
Content-Disposition: form-data; name="file"; filename="portainer.db.tar.gz"
Content-Type: application/gzip
[Gzip compressed binary database content]
-----------------------------987654321--This method is highly effective for maintaining persistence while avoiding obvious markers. The attacker controls not only the Portainer instance but also inherits any configurations related to target endpoints or managed clusters.
The security impact of CVE-2026-55761 is marked as High. On the CVSS v4.0 scale, it receives a base score of 7.1. The vector string CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:H/SI:H/SA:H indicates that while the compromise of the initial deployment context (the host running Portainer itself) has limited direct integrity impacts, the downstream systems managed by Portainer (Subsequent Confidentiality, Integrity, and Availability) are completely compromised.
In typical deployments, Portainer manages high-privilege access points. For Docker environments, this often includes mounting the host Docker UNIX socket (/var/run/docker.sock). An attacker with administrative control over Portainer can execute arbitrary containers with the --privileged flag, enabling complete escape to the host operating system and allowing them to run code as root.
For Kubernetes environments, Portainer administrators hold service account credentials capable of creating, modifying, and destroying resources across the cluster. This lets attackers deploy malicious pods, steal secrets, or configure lateral movement paths across the internal corporate network. The compromise of a single uninitialized Portainer instance therefore scales directly to a full infrastructure compromise.
The primary remediation strategy is upgrading Portainer Community Edition to a patched release. Portainer released patches in version 2.39.4 and version 2.43.0 to resolve this vulnerability. These versions enforce the setup token gate by default on all uninitialized deployments.
If immediate patching is not possible, security teams must deploy strict mitigating controls. Network segmentation should be used to restrict access to Portainer setup ports (9000 and 9443) during the initial container deployment phase. Deploying the application within an isolated VLAN or binding the ports to localhost ensures only local system administrators can perform the setup.
# docker-compose.yml modification for secure initialization
services:
portainer:
image: portainer/portainer-ce:2.43.0
ports:
- "127.0.0.1:9443:9443"
environment:
- PORTAINER_SETUP_TOKEN=SecureCustomInitializationToken_2026Administrators can also specify a custom setup token upon container execution via environment variables or CLI flags. This prevents Portainer from falling back to log-based token printing and secures the setup phase. Do not configure containers with --no-setup-token or PORTAINER_NO_SETUP_TOKEN=true, as these options completely disable the authentication gate.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:H/SI:H/SA:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Portainer Community Edition Portainer | >= 2.39.0, < 2.39.4 | 2.39.4 |
Portainer Community Edition Portainer | >= 2.40.0, < 2.43.0 | 2.43.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-287 |
| Attack Vector | Network |
| CVSS Score | 7.1 |
| EPSS Score | 0.00493 |
| EPSS Percentile | 40.19% |
| Impact | Full Administrative Takeover |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not listed |
The software does not prove or insufficiently proves that a claim of identity is correct before allowing access to resources or functionality.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.
CVE-2026-70035 is a high-severity Remote Code Execution (RCE) vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio caused by improper handling during the deserialization of untrusted data (CWE-502). An unauthenticated attacker can execute arbitrary code within the context of the affected process by delivering a crafted serialized payload.