← Back to Feed CACHED · 2026-10-14 03:29:26 · CACHE_KEY CVE-2026-105445
CVE-2026-105445 · CWE-266 · Disclosed 2026-10-13

Docker Hub Account-to-Organization Conversion Silent PAT Transfer (GhostPat)

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Like someone cloning your house key during a change-of-address form and the post office never telling you

CVE-2026-105445 — dubbed GhostPat by its discoverers — is an Incorrect Privilege Assignment flaw (CWE-266) in Docker Hub's account-to-organization conversion flow. When a personal account is converted to an organization, the platform requires nominating an existing user as the new org owner. From August 26, 2024 through September 30, 2026, Docker Hub silently *transferred* the converting account's personal access tokens (PATs) to the nominated owner — without rotating the token secret, without notifying the recipient, and without requiring their consent. An attacker who created a throwaway account, minted a PAT with read,write,delete scopes, and converted that account while naming any target username as owner would retain a live credential that authenticated to the Docker registry *as the victim*. That PAT could then pull private images, push malicious images into the victim's namespace, and delete existing tags — all within organizations where the victim had membership. The exposure window lasted roughly 25 months and affected every Docker Hub user, though Docker's forensic review of ~304,000 token-associated pushes found no evidence of exploitation.

There is no official vendor CVSS for this CVE. Third-party trackers assigned CVSS 4.0 scores around 9.4 (Critical). That number is directionally correct given the trivial attack complexity (a free account and three API calls), zero victim interaction, and the platform's role as the world's largest container registry (~20M+ developers, ~11B monthly pulls). The blast radius is supply-chain-scale: a pushed poisoned image under a trusted namespace could propagate to thousands of CI/CD pipelines within hours. The one major caveat is that Docker has already remediated this server-side — the conversion flow no longer transfers PATs, and all historically transferred tokens were force-deactivated on October 8, 2026. There is nothing to "patch" on your hosts. Your job is verification and defense-in-depth.

"Any Docker Hub user could silently push images as any other user for 25 months. Supply-chain nightmare, now fixed server-side."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Create throwaway Docker Hub account

The attacker registers a new personal Docker Hub account. This is free, requires only an email address, and can be done in under a minute. No identity verification or payment is required. The attacker chooses any available username.
Conditions required:
  • Valid email address
  • Internet access
Where this breaks in practice:
  • None — Docker Hub registration is open and free
Detection/coverage: No detection opportunity; legitimate account creation is indistinguishable from attacker setup.
STEP 02

Generate a PAT with broad scopes

The attacker creates a personal access token on the throwaway account with Read, Write, Delete permissions. The PAT secret is stored locally. This is a standard Docker Hub feature available to all users via the web UI or API. The attacker records the dckr_pat_* token value.
Conditions required:
  • Authenticated throwaway Docker Hub account
Where this breaks in practice:
  • None — PAT creation is a standard self-service feature
Detection/coverage: No detection opportunity from the victim's side; PAT creation is an operation on the attacker's own account.
STEP 03

Convert account to organization, nominate victim as owner

The attacker initiates the account-to-organization conversion via Docker Hub settings, specifying the victim's Docker Hub username as the new organization owner. The platform does not send the victim a notification, does not require the victim's consent, and does not validate the relationship between the two accounts. The conversion completes immediately.
Conditions required:
  • Knowledge of victim's Docker Hub username (public information)
Where this breaks in practice:
  • The attacker's throwaway account is consumed (converted to an org) — a one-time cost per target
Detection/coverage: No notification to victim. Docker Hub audit logs (if enterprise tier) might show the org ownership change, but the token transfer itself was invisible.
STEP 04

PAT silently transfers to victim's identity

Docker Hub's backend reassigns the PAT's owner_id to the victim's account without rotating or revoking the token secret. The attacker's locally stored dckr_pat_* value now authenticates to the Docker registry as the victim. The token inherits the victim's repository permissions and organization memberships. The victim has no way to know this happened — no email, no UI indicator, no audit event.
Conditions required:
  • Vulnerable Docker Hub backend (Aug 26 2024 – Sep 30 2026)
Where this breaks in practice:
  • Fixed server-side on Sep 30, 2026 — this step is no longer possible
Detection/coverage: Docker's post-incident forensic review of 304,000 pushes was the only detection mechanism. No real-time alerting existed.
STEP 05

Authenticate and operate as victim

The attacker runs docker login -u <victim_username> using the transferred PAT as the password. The registry accepts the credential and issues a bearer token scoped to the victim's access. The attacker can now docker push malicious images to the victim's repositories (including org repos), docker pull private images, or delete tags via the registry API. If the victim maintains popular public images, the attacker can replace them with backdoored versions.
Conditions required:
  • Retained PAT secret from Step 2
  • Target user has repositories or org memberships worth exploiting
Where this breaks in practice:
  • Attacker must know which repos/tags matter for supply-chain impact
  • Content Trust / image signing (if enabled by consumers) would flag unsigned pushes
Detection/coverage: Docker Hub access logs (enterprise tier) would show registry operations from an unexpected IP. Image digest changes would be visible to consumers using --digest pinning or Notary/cosign verification.
03 · Compensating Control

1
CRITICAL 9.2→LOW 2.0
SEVERITY REDUCED
Audit and revoke unrecognized Docker Hub PATs immediately — Log into Docker Hub → Account Settings → Personal Access Tokens. Revoke any token you do not recognize. Docker notified affected users on Oct 2 and force-deactivated transferred PATs on Oct 8, but some users may have opted out of deactivation. Per the noisgate mitigation SLA for CRITICAL, complete this within 3 days. This directly breaks Step 5 of the attack path by invalidating the credential the attacker would use.
2
CRITICAL 9.2→MEDIUM 4.5
SEVERITY REDUCED
Verify container image integrity for images pulled Aug 2024 – Oct 2026 — For any container image pulled from Docker Hub during the 25-month exposure window, compare the image digest (sha256:...) against the publisher's known-good manifest or signed metadata. Use docker image inspect --format='{{.RepoDigests}}' or query your registry mirror's catalog. Prioritize base images and any image used in production Dockerfiles. Deploy within 3 days per noisgate mitigation SLA. This detects whether Step 5 resulted in tampered images reaching your fleet.
3
CRITICAL 9.2→LOW 2.5
SEVERITY REDUCED
Enable Docker Content Trust and cosign verification in CI/CD — Set DOCKER_CONTENT_TRUST=1 in CI/CD pipelines and enforce cosign verify or Notary v2 signature checks before deploying any image. This prevents future supply-chain attacks via unsigned image pushes regardless of registry-side flaws. Deploy within 90 days per noisgate remediation SLA. This adds a verification layer at Step 5 that the attacker cannot bypass without the publisher's signing key.
4
CRITICAL 9.2→LOW 2.0
SEVERITY REDUCED
Pin images by digest, not tag, in all Dockerfiles and Helm charts — Replace FROM nginx:latest with FROM nginx@sha256:<known-good-digest>. Tag-based pulls are mutable and would have silently consumed a poisoned image; digest-pinned pulls are immutable. Audit all Dockerfiles, docker-compose files, Helm values, and Kubernetes manifests. Deploy within 90 days per noisgate remediation SLA. This breaks the supply-chain propagation at pull time.
5
CRITICAL 9.2→LOW 1.5
SEVERITY REDUCED
Use organization access tokens instead of personal PATs for automation — Docker recommends replacing personal PATs used in CI/CD with organization-scoped access tokens, which are not subject to the account-conversion transfer mechanism. Audit all service accounts and CI/CD secrets for dckr_pat_* values tied to personal accounts. Deploy within 30 days. This eliminates the class of credential that was vulnerable.
What doesn't work
  • Patching your hosts — this is a SaaS-side vulnerability in Docker Hub's backend. There is no client-side patch, Docker Engine update, or OS patch that addresses this. Docker already fixed it server-side.
  • Rotating your own PATs — rotating tokens *you* created does not help because the attack vector was *incoming* tokens transferred *to* you by an attacker. You need to identify and revoke tokens you do NOT recognize.
  • Network-level controls (WAF, firewall, IDS) — the attack occurs entirely within Docker Hub's SaaS infrastructure via legitimate API calls. No network appliance in your environment can intercept or block it.
  • Docker Hub 2FA/SSO — the vulnerability operated at the registry API layer using PATs, which bypass interactive authentication entirely. Having 2FA enabled on your Docker Hub account would not have prevented a transferred PAT from authenticating.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo evidence. Docker reviewed ~304,000 pushes made with transferred tokens and found no malicious activity — no malware, cryptominers, C2, or credential harvesting. Not listed in CISA KEV.
Proof of ConceptFully demonstrated by researchers Peter Molnar and Krisztian Hozak (mptrdev.com). Technical writeup at ghostpat.com. They reproduced the full chain: login as victim, push to victim's namespace, pull victim's private repos, inherit org memberships. No public exploit code needed — the "exploit" is three clicks in Docker Hub settings.
EPSS ScoreNot yet scored — CVE published October 13, 2026; EPSS data typically lags 24-48 hours. Expected to be low given SaaS-side remediation.
KEV StatusNot listed. No evidence of exploitation; Docker's forensic review found all 304k pushes were to converted org's own repos.
CVSS VectorNo official vendor score. Third-party (Strix.ai/CVEMon): CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H → 9.4 Critical. Network-accessible, low privilege (free account), no user interaction, high downstream impact across confidentiality/integrity/availability.
Affected RangeDocker Hub SaaS platform, backend deployments from 2024-08-26T20:33Z through 2026-09-30T19:10Z (~25 months). Not a versioned software product — all Docker Hub users were in scope.
Fixed VersionServer-side fix deployed Sep 30, 2026 (~4 hours after report). Transferred PATs force-deactivated Oct 8, 2026. No client-side patch exists or is needed. Docker now requires owner consent for conversions, logs token ownership changes.
Scanning / ExposureNot applicable for traditional scanners (Shodan/GreyNoise/Censys). This is a SaaS-side logic flaw in Docker Hub's IAM layer, not an exposed service. Every Docker Hub account holder (~20M+ developers) was a potential victim during the exposure window.
Disclosure DateReported Sep 30, 2026 via [email protected]. Public advisory Oct 13, 2026. Affected user notification Oct 2, 2026.
ResearchersPeter Molnar and Krisztian Hozak of mptrdev.com. Writeup: ghostpat.com. Reported through Docker's Vulnerability Disclosure Program.

Sources.

  1. Docker Official Security Advisory
  2. GhostPat — Researcher Writeup (Molnar & Hozak)
  3. BleepingComputer — Docker Hub Flaw Analysis
  4. The Hacker News — Docker Fixes Critical Two-Year-Old Hub Flaw
  5. SecurityOnline — CVE-2026-105445 Token Flaw Advisory
  6. Strix.ai — CVE-2026-105445 CVSS 9.4 Detail
  7. CVEMon — CVE-2026-105445 Overview and Trends
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.2/10)

Why this verdict

  • Supply-chain registry = CRITICAL floor: Docker Hub is the canonical container artifact store — it is listed in the high-value-role catalog under *CI/CD + artifact stores* and *supply-chain registries*. With ~20M developers and ~11B monthly image pulls, ≥10% of its installed base (arguably 100%) occupies the high-value role by definition. A successful push of a poisoned image to a trusted namespace propagates to CI/CD pipelines, production Kubernetes clusters, and developer workstations fleet-wide. The blast radius is supply-chain-scale. This floors the verdict at CRITICAL.
  • Role multiplier: (a) Low-value: personal hobby repos — compromise is annoying but contained. (b) Typical: team/org repos for internal services — compromise delivers backdoored containers to staging and prod. (c) High-value: official images, popular community images (e.g., nginx, node, python), base images consumed by thousands of downstream Dockerfiles — compromise here is SolarWinds-class. The chain succeeds identically in all three roles; the blast radius in role (c) is supply-chain-scale. Since Docker Hub *is* the supply-chain registry, this is a canonical high-value component and the CRITICAL floor applies.
  • Trivial exploitation, zero friction in the chain: The entire attack requires a free Docker Hub account, three API calls (register, create PAT, convert account), and knowledge of the victim's public Docker Hub username. No victim interaction. No elevated privileges. No network position. No exploit code. The CVSS 4.0 third-party score of 9.4 reflects this accurately.
  • 25-month exposure window amplifies risk: The flaw existed from August 2024 to September 2026. Even though Docker's forensic review found no exploitation, the *possibility* that images in your environment were tampered with during this window demands verification. Absence of evidence is not evidence of absence when the detection mechanism (Docker's own retrospective audit) is the only source.
  • No downward friction adjustment: Unlike on-premise CVEs where network segmentation, authentication barriers, or EDR create friction, this is a SaaS-side logic flaw. Every Docker Hub user was reachable. The only friction is that the attacker's throwaway account is consumed per target — a negligible cost.

Why not higher?

A 9.2 already reflects near-maximum severity. The gap from 10.0 accounts for two factors: (1) the attack scope was limited to registry-level operations — no web UI access, no account takeover, no password/MFA changes — so the attacker could not persist beyond the transferred PAT's lifetime or escalate to full account control; and (2) Docker Content Trust and digest-pinned pulls, while rarely enabled, would have detected or blocked poisoned images for the small subset of consumers using them.

Why not lower?

Downgrading below CRITICAL would require evidence that the supply-chain blast radius is unreachable, but it is not — the chain succeeds with zero friction against any Docker Hub user, including maintainers of official and popular images consumed by millions. The SaaS-side remediation eliminates *future* exploitation but does not retroactively guarantee image integrity for the 25-month exposure window. The CRITICAL floor for a supply-chain registry component holds.

06 · Verification

Crowdsourced verification payload.

Run on any workstation with Python 3.7+ and the requests library. Invoke: python verify_ghostpat.py <docker_hub_username> <your_pat_or_password>. Requires read access to your Docker Hub account (any valid credential). The script lists all PATs on the account and flags any created during the 25-month exposure window that remain active.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
"""CVE-2026-105445 (GhostPat) verification script.
Checks a Docker Hub account for PATs created during the exposure window
(Aug 26 2024 – Sep 30 2026) that may have been silently transferred.
Outputs: VULNERABLE / PATCHED / UNKNOWN
Exit codes: 1 = VULNERABLE, 0 = PATCHED, 2 = UNKNOWN
"""
import sys
import json
try:
    import requests
except ImportError:
    print("UNKNOWN - 'requests' library not installed. Run: pip install requests")
    sys.exit(2)
from datetime import datetime, timezone

VULN_START = datetime(2024, 8, 26, tzinfo=timezone.utc)
VULN_END   = datetime(2026, 9, 30, 19, 10, 0, tzinfo=timezone.utc)
HUB_LOGIN  = "https://hub.docker.com/v2/users/login"
HUB_PATS   = "https://hub.docker.com/v2/access-tokens"

def main():
    if len(sys.argv) < 3:
        print("Usage: python verify_ghostpat.py <username> <password_or_pat>")
        sys.exit(2)

    username = sys.argv[1]
    password = sys.argv[2]

    # Authenticate to Docker Hub
    try:
        r = requests.post(HUB_LOGIN, json={"username": username, "password": password}, timeout=15)
    except requests.RequestException as e:
        print(f"UNKNOWN - HTTP error during login: {e}")
        sys.exit(2)

    if r.status_code != 200:
        print(f"UNKNOWN - Docker Hub login failed (HTTP {r.status_code}). Check credentials.")
        sys.exit(2)

    token = r.json().get("token", "")
    if not token:
        print("UNKNOWN - No token returned from Docker Hub login.")
        sys.exit(2)

    headers = {"Authorization": f"Bearer {token}"}

    # Fetch PATs
    try:
        r = requests.get(HUB_PATS, headers=headers, timeout=15)
    except requests.RequestException as e:
        print(f"UNKNOWN - HTTP error fetching PATs: {e}")
        sys.exit(2)

    if r.status_code != 200:
        print(f"UNKNOWN - Failed to list PATs (HTTP {r.status_code}). "
              f"API may require Docker Pro/Team/Business plan.")
        sys.exit(2)

    pats = r.json().get("results", [])
    if not pats:
        print("PATCHED - No personal access tokens found on this account.")
        sys.exit(0)

    suspicious = []
    for pat in pats:
        created_str = pat.get("created_at", "")
        is_active = pat.get("is_active", False)
        if not created_str:
            continue
        try:
            created = datetime.fromisoformat(created_str.replace("Z", "+00:00"))
        except ValueError:
            continue
        if VULN_START <= created <= VULN_END and is_active:
            suspicious.append({
                "uuid": pat.get("uuid", "N/A"),
                "description": pat.get("token_label", pat.get("description", "N/A")),
                "created": created_str,
                "scopes": pat.get("scopes", []),
                "last_used": pat.get("last_used", "never")
            })

    if suspicious:
        print("VULNERABLE - Active PATs found created during CVE-2026-105445 exposure window:")
        for s in suspicious:
            print(f"  UUID: {s['uuid']}  Label: {s['description']}  "
                  f"Created: {s['created']}  Scopes: {s['scopes']}  "
                  f"Last used: {s['last_used']}")
        print(f"\nAction: Review these {len(suspicious)} token(s). Revoke any you did not create.")
        print("Docker force-deactivated transferred PATs on Oct 8 2026, but opt-outs are possible.")
        sys.exit(1)
    else:
        print(f"PATCHED - {len(pats)} PAT(s) found, none active within the exposure window "
              f"(Aug 26 2024 - Sep 30 2026). Docker's server-side fix is in effect.")
        sys.exit(0)

if __name__ == "__main__":
    main()