Apr 11, 2026·7 min read·5 visits
A race condition in Juju's API server allows authenticated users to crash the server or replay authentication tokens due to thread-unsafe map operations.
Canonical Juju is affected by a medium-severity race condition vulnerability (CWE-362) within its API server. The vulnerability allows an authenticated attacker to trigger concurrent memory access violations in the Go runtime, resulting in an unrecoverable fatal panic and Denial of Service (DoS), or to bypass single-use token constraints via an authentication replay attack.
Canonical Juju is affected by a medium-severity race condition vulnerability tracked as CVE-2026-5774. The flaw resides within the API server component, specifically in the management of macaroon discharge tokens during the local login process. An authenticated attacker can exploit this vulnerability to induce a Denial of Service (DoS) state or bypass single-use token restrictions.
The root of the issue is categorized as CWE-362, concurrent execution using a shared resource with improper synchronization. The API server stores discharge tokens in a standard data structure that lacks inherent concurrency protections. When the server processes simultaneous HTTP requests, these requests execute in isolated goroutines but interact with the same underlying token storage mechanism.
This architectural choice creates an exposed attack surface on the /local-login/discharge and /local-login/form endpoints. A threat actor with valid credentials can dispatch carefully timed network requests to these endpoints, intentionally triggering the concurrency limits of the runtime environment. The resulting state corruption forces the API server process to terminate, disrupting management access to the Juju environment.
The vulnerability originates from the unsafe concurrent access patterns applied to a native Go map[string]string structure. In the Juju API server codebase, the localLoginHandlers struct utilizes a userTokens map to track active macaroon discharge tokens. The Go runtime mandates that map implementations are not thread-safe and strictly prohibits simultaneous memory access involving at least one write operation.
When the runtime detects a concurrent write, or a concurrent read and write against the same map instance, it triggers a fatal, non-recoverable panic. The Juju API server triggers this condition because the formHandler function continuously writes new authentication tokens to the userTokens map upon successful login events. Concurrently, the checkThirdPartyCaveat function reads tokens from the map for verification and subsequently issues a delete operation to ensure the token cannot be reused.
Because the Go HTTP server implementation handles each incoming request in a separate goroutine, high-frequency requests directly translate to concurrent execution threads. The absence of a mutual exclusion lock (mutex) around the userTokens map guarantees that these independent goroutines will eventually intersect during memory access.
Furthermore, the logic implemented in the checkThirdPartyCaveat function exhibits a Time-of-Check to Time-of-Use (TOCTOU) condition. The function first queries the map to verify token existence and then executes the deletion operation in a separate, non-atomic step. This execution gap allows two parallel verification threads to read the same token successfully before either thread removes the token from memory.
The codebase lacked synchronization primitives in the authentication handlers prior to the patch. The userTokens map was declared directly within the localLoginHandlers struct without an associated mutex. Token validation and deletion were performed sequentially in the caveat checker, exposing the TOCTOU window.
// Vulnerable implementation
type localLoginHandlers struct {
authCtxt *authContext
finder state.EntityFinder
userTokens map[string]string
}
// Non-atomic read and delete operation inside caveat checker
username, ok := h.userTokens[tokenString]
delete(h.userTokens, tokenString)The remediation introduced in commit 2bc884d34dce1a72f93e67202c1fd1385ad474b0 resolves the issue by adding a sync.Mutex field named tokenMutex to the struct. The developers refactored the map operations into dedicated, thread-safe methods. The getUserFromToken method now locks the mutex before accessing the map, reads the token, deletes it, and unlocks the mutex via a defer statement.
// Patched implementation
type localLoginHandlers struct {
authCtxt *authContext
finder state.EntityFinder
tokenMutex sync.Mutex
userTokens map[string]string
}
// Thread-safe, atomic read and delete operation
func (h *localLoginHandlers) getUserFromToken(token string) (string, bool) {
h.tokenMutex.Lock()
defer h.tokenMutex.Unlock()
username, ok := h.userTokens[token]
delete(h.userTokens, token)
return username, ok
}The following diagram illustrates the thread safety implementation introduced by the patch, ensuring that concurrent goroutines are sequentially gated.
An authenticated threat actor can exploit this vulnerability through the network by executing a race condition attack. The primary attack vector targets the availability of the API server. By generating a high volume of simultaneous HTTP requests to the /local-login/form and /local-login/discharge endpoints, the attacker forces the server to process multiple map operations simultaneously.
This deliberate flooding guarantees that at least two goroutines will attempt to modify or read the userTokens map at the exact same instruction cycle. The Go runtime detects this specific violation and intentionally crashes the application process with a fatal error: concurrent map writes message. The API server remains offline until restarted by the orchestration layer.
Alternatively, an attacker can exploit the TOCTOU behavior to replay authentication tokens. The attacker obtains a single valid macaroon discharge token via standard authentication procedures. The attacker then scripts two distinct HTTP clients to transmit the same token to the caveat checker endpoint at precisely the same time.
If the execution timing aligns properly within the vulnerable window, both goroutines evaluate the token presence check as true. The system authenticates both sessions before either goroutine advances to the deletion instruction. This technique bypasses the intended security design that explicitly mandates discharge tokens must be consumed exactly once.
The vulnerability presents two distinct impacts: total loss of availability for the API service and subversion of authentication mechanisms. The availability impact is classified as high because the resulting Go runtime panic is fatal. The process termination cannot be intercepted or handled by application-level error recovery routines, requiring a full process restart to restore service.
During the period the API server is offline, administrators lose management access to the Juju environment. Automated orchestration tasks, deployment processes, and health monitoring mechanisms dependent on the API fail to execute. This disruption scales in severity based on the operational reliance on the Juju controller.
The confidentiality and integrity impact resulting from the token replay vector is constrained by the attacker's existing privilege level. The attacker must already possess valid credentials to generate the initial discharge token. Replaying the token allows the generation of parallel authenticated sessions, but does not inherently escalate privileges beyond the permissions associated with the original compromised account.
The Common Vulnerability Scoring System (CVSS) v4.0 evaluation yields a score of 6.1 (Medium). The vector string CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N accurately reflects the network attack surface, the high complexity of timing the race condition, and the high impact on target availability.
Canonical resolved this vulnerability in three specific patch branches across the Juju ecosystem. Administrators operating Juju 4.0.x installations must upgrade to version 4.0.6 to secure the API server. Deployments running the Juju 3.6.x branch require an upgrade to version 3.6.21. Legacy installations on the Juju 2.9.x branch must update to version 2.9.57.
Organizations that cannot immediately apply the vendor patches should implement network-level rate limiting against the Juju controller interfaces. Throttling incoming connections to the /local-login/discharge and /local-login/form endpoints reduces the probability of concurrent execution overlap. While rate limiting does not eliminate the root cause, it significantly increases the attack complexity required to trigger the concurrency panic.
Security operations centers should deploy detection mechanisms targeting API server logs and network traffic patterns. Teams must monitor the application runtime logs for fatal error: concurrent map writes events, which serve as a definitive indicator of compromise or active exploitation attempts. High-frequency request clusters originating from a single authenticated source address should trigger automated defensive blocks.
CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Juju Canonical | 2.0.0 to < 2.9.57 | 2.9.57 |
Juju Canonical | 3.0.0 to < 3.6.21 | 3.6.21 |
Juju Canonical | 4.0.0 to < 4.0.6 | 4.0.6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-362 |
| Attack Vector | Network |
| CVSS v4.0 Score | 6.1 (Medium) |
| Impact | Denial of Service (DoS) and Authentication Replay |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
Concurrent Execution using Shared Resource with Improper Synchronization