Jan 27, 2026·5 min read·15 visits
Dozzle v9.0.2 implemented access control on the front door but left the back window open. While the UI filtered which containers you could see in a list, the backend API for retrieving a specific container by ID completely ignored your access permissions. This allows any authenticated user to access, view logs, and execute commands on any container managed by Dozzle agents, regardless of their assigned restrictions.
A critical authorization bypass in Dozzle's agent service allows restricted users to access any container by ID, effectively rendering Label-Based Access Control (LBAC) useless for direct lookups.
Dozzle is the darling of the Docker world for developers who want a lightweight, no-nonsense log viewer. It’s fast, it’s real-time, and it doesn't require a database. But like any tool that sits on top of the Docker socket, it wields immense power. If you control Dozzle, you control the containers. To mitigate this risk in multi-tenant or multi-team environments, Dozzle introduced Label-Based Access Control (LBAC).
The idea is simple: You tag your containers with labels like env=dev or env=prod. You then tell Dozzle, "User Alice is only allowed to see containers where env=dev." In theory, this keeps Alice from accidentally (or intentionally) rm -rf /'ing the production database. It’s a velvet rope that separates the interns from the critical infrastructure.
But here’s the thing about velvet ropes: they only work if the bouncer actually checks who walks past them. CVE-2026-24740 is the story of a bouncer who was handed a list of banned guests, nodded politely, and then let everyone in anyway because they knew the secret password (the Container ID).
The vulnerability lies deep within the agentService logic. Dozzle supports a distributed mode where a master instance talks to agents running on other nodes. When a user logs in, their session is stamped with specific filter rules (the LBAC labels).
When the UI requests a list of containers, the system correctly applies these filters. Alice only sees her dev containers. The UI looks secure. The list is curated. Everything seems fine.
However, when the UI (or an attacker) requests a specific container by its ID—for example, to stream logs or start an interactive shell—the application calls a method named FindContainer. This method accepts the Container ID and the User's Labels. The expectation is that the method will say, "Okay, find container X, but only if it matches these labels."
In reality, the code treated the labels like a suggestion. The implementation for the agent service accepted the labels as an argument but failed to pass them along in the gRPC request to the remote agent. The agent, which has no knowledge of the user's session state, simply received a command: "Give me the handle for Container ID prod-db-123." Being a compliant piece of software, it obliged.
Let's look at the Go code responsible for this mishap. This is a textbook example of a function signature promising security checks that the implementation fails to deliver.
The Vulnerable Code (v9.0.2):
In internal/support/container/agent_service.go, the FindContainer method takes labels as an argument, but watch where that variable goes (spoiler: nowhere).
func (a *agentService) FindContainer(ctx context.Context, id string, labels container.ContainerLabels) (container.Container, error) {
// The 'labels' parameter contains the security restrictions.
// Notice how it is completely ignored in the function call below?
return a.client.FindContainer(ctx, id)
}The Fix (v9.0.3): The patch involves properly serializing these labels into the gRPC request so the agent can enforce the filter. The developer had to update the client definition to accept the filter and pack it into the protobuf message.
// internal/agent/client.go in commit 620e59a
func (c *Client) FindContainer(ctx context.Context, id string, labels container.ContainerLabels) (container.Container, error) {
in := &pb.FindContainerRequest{ContainerId: id}
// Now we actually use the labels!
if labels != nil {
in.Filter = make(map[string]*pb.RepeatedString)
for k, v := range labels {
in.Filter[k] = &pb.RepeatedString{Values: v}
}
}
return c.client.FindContainer(ctx, in)
}This is a classic "Interface Disconnect." The architect likely designed the interface to be secure, but the plumbing didn't connect the pipes.
Exploiting this requires an authenticated session, but it doesn't require high privileges. You just need to be a user with some access. Here is how a malicious "intern" could pivot to "admin":
dev-app containers. They want to access prod-payment-gateway.docker-compose.yml file committed to a shared repo).# Standard request to attach to a stream
GET /api/logs/stream/e3b0c44298fc?stdout=true&stderr=true HTTP/1.1
Cookie: auth=... (Valid Token with 'dev' scope)Because the backend FindContainer logic drops the 'dev' scope requirement when talking to the agent, the agent returns the stream for e3b0c44298fc (the Prod DB). The attacker now has a live view of production logs.
Escalation to RCE:
Dozzle isn't just a viewer. It supports exec. If the attacker sends a request to /api/containers/e3b0c44298fc/exec, the same flaw applies. They now have a root shell inside the production container.
This vulnerability turns a "Least Privilege" model into a "Most Privilege" suggestion. In many DevOps environments, developers are given read-only access to logs to debug issues. This CVE upgrades that access to full interactive control.
With exec access to a container, an attacker can:
Additionally, a secondary flaw fixed in the same release (Commit 58caca2) involved a log.Fatal call when parsing invalid filters. This meant that even if an attacker couldn't guess an ID, they could crash the entire Dozzle service just by sending a malformed header, denying access to everyone else.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Dozzle amir20 | <= 9.0.2 | 9.0.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-285 |
| Attack Vector | Network |
| CVSS | 8.8 (High) |
| Impact | Confidentiality, Integrity, Availability |
| Exploit Status | PoC Available |
| Architecture | Agent/Client gRPC |
Improper Authorization
GHSA-268h-hp4c-crq3 is a Carriage Return Line Feed (CRLF) injection vulnerability in the Nodemailer npm package affecting versions up to and including 8.0.8. The library allows arbitrary email header injection when parsing user-controlled comments within list headers (such as List-Unsubscribe or List-ID). This occurs because list headers bypass standard validation by utilizing an internal 'prepared' flag, causing unsanitized newlines to be emitted directly into the outgoing RFC822 mail stream. This exploit allows remote attackers to inject custom, unauthorized mail headers, disrupting signature checks, bypassing filters, or spoofing parameters.
A logic flaw in PyJWT's PyJWKClient class allows remote unauthenticated attackers to trigger a complete authentication outage. By transmitting a volume of JWTs containing randomized, non-existent Key ID (kid) values, attackers force synchronous outbound JWKS resolution queries. When these queries fail or time out, a defect in the error cleanup code overwrites the local cache of valid signing keys with None, causing a denial of service.
A high-severity type-confusion path traversal vulnerability (CVE-2026-49982 / GHSA-7c78-jf6q-g5cm) exists in the node-tmp package version 0.2.6. The vulnerability allows remote attackers to bypass path validation checks by passing non-string data types such as Arrays or duck-typed Objects into options like prefix, postfix, or template. Because the library relies on the .includes() method without verifying the input type, standard array checks evaluate differently than string checks. Downstream string coercion subsequently restores the traversal sequence, allowing files and directories to be created outside the designated temporary directory root. This can result in arbitrary file writes and potential local file execution depending on application context.
CVE-2026-47347 is an open redirect vulnerability affecting multiple TYPO3 CMS versions. The issue resides in GeneralUtility::sanitizeLocalUrl, where an insufficient blocklist validation implementation fails to prevent browsers from normalizing malformed relative paths into external protocol-relative redirections. Attackers can exploit this to conduct phishing, session hijacking, or credential harvesting campaigns.
An authenticated backend user with access to the Recycler module in TYPO3 CMS can bypass write restrictions and restore soft-deleted records on pages or database tables they are not authorized to modify. This vulnerability resides in the core DataHandler class due to missing permission checks during 'undelete' operations.
CVE-2026-11607 is a critical broken access control vulnerability in TYPO3 CMS's Form Framework (ext:form). Authenticated backend users with access to the Form Framework can load unauthorized YAML configurations, bypassing file extension restrictions. This allows the execution of arbitrary SQL commands via the SaveToDatabase finisher, leading to privilege escalation to administrator level.