GHSA-QHH4-458H-XWH2

Vulnerability from github – Published: 2026-05-08 20:06 – Updated: 2026-05-08 20:06
VLAI
Summary
@cyclonedx/cdxgen: Docker registry auth substring match forwards credentials to a different registry
Details

Docker registry auth substring match forwards credentials to a different registry

Repository

cdxgen/cdxgen

Affected product/package

  • Ecosystem: npm
  • Package: @cyclonedx/cdxgen
  • Reviewed tree version: 12.3.3
  • Reviewed commit: b1e179869fd7c6032c3d483c3f7bd4d7154ec22b
  • Affected file: lib/managers/docker.js
  • Affected from: v9.9.5

The Single Executable Applications (SEA) binaries and container images are also affected.

Weakness

CWE-522 / CWE-346.

Summary

When cdxgen scans or pulls container images through the Docker daemon API, it builds an X-Registry-Auth header from Docker credentials in DOCKER_CONFIG/config.json. The credential selection logic matches configured registry keys with substring checks:

if (forRegistry && !serverAddress.includes(forRegistry)) {
  continue;
}

This is not an origin-safe registry comparison. For example, credentials configured for private-registry.example.com are selected for a requested image under registry.example.com, because:

"private-registry.example.com".includes("registry.example.com") === true

The selected credentials are then serialized into X-Registry-Auth for the Docker API pull request targeting the requested registry.

Reproduction

Use the attached/local proof:

node submissions/github-gsa/cdxgen-docker-registry-auth-substring-forwarding/evidence/cdxgen_docker_registry_auth_substring_probe.mjs

The proof is fully local. It creates a temporary Docker config containing credentials for private-registry.example.com, starts a localhost mock Docker API endpoint, sets DOCKER_HOST to that endpoint, then calls cdxgen's exported Docker request path for a pull from registry.example.com.

Observed vulnerable output:

{
  "decision": "GO",
  "dockerConfigAuthHost": "private-registry.example.com",
  "requestedRegistry": "registry.example.com",
  "substringMatch": true,
  "dockerApiUrl": "/images/create?fromImage=registry.example.com/team/app:latest",
  "headerPresent": true,
  "decodedHeader": {
    "username": "trusted-user",
    "password": "trusted-pass",
    "serveraddress": "private-registry.example.com"
  }
}

Impact

If an operator has Docker credentials for a private registry and uses cdxgen to scan an image from a different registry whose hostname is a substring of that private registry hostname, cdxgen can attach the private registry credentials to the Docker pull request for the different registry.

In a realistic attack, an attacker who controls or can observe the requested registry can induce a victim to scan an image from that registry. The Docker daemon API receives an X-Registry-Auth payload containing credentials for the victim's private registry but associated with the attacker-requested pull. This is a credential forwarding/misbinding issue in cdxgen's container image handling.

References

Functions normalizeRegistryHost and registriesMatch added to normalize and perform strict host matching.

Fix PR: https://github.com/cdxgen/cdxgen/pull/3964

Researcher: Francesco SabiuResearcher: Francesco Sabiu

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@cyclonedx/cdxgen"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "9.9.5"
            },
            {
              "fixed": "12.3.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-346",
      "CWE-522"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-08T20:06:00Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "# Docker registry auth substring match forwards credentials to a different registry\n\n## Repository\n\n`cdxgen/cdxgen`\n\n## Affected product/package\n\n- Ecosystem: npm\n- Package: `@cyclonedx/cdxgen`\n- Reviewed tree version: `12.3.3`\n- Reviewed commit: `b1e179869fd7c6032c3d483c3f7bd4d7154ec22b`\n- Affected file: `lib/managers/docker.js`\n- Affected from: v9.9.5\n\nThe Single Executable Applications (SEA) binaries and container images are also affected.\n\n## Weakness\n\nCWE-522 / CWE-346.\n\n## Summary\n\nWhen cdxgen scans or pulls container images through the Docker daemon API, it builds an `X-Registry-Auth` header from Docker credentials in `DOCKER_CONFIG/config.json`. The credential selection logic matches configured registry keys with substring checks:\n\n```js\nif (forRegistry \u0026\u0026 !serverAddress.includes(forRegistry)) {\n  continue;\n}\n```\n\nThis is not an origin-safe registry comparison. For example, credentials configured for `private-registry.example.com` are selected for a requested image under `registry.example.com`, because:\n\n```js\n\"private-registry.example.com\".includes(\"registry.example.com\") === true\n```\n\nThe selected credentials are then serialized into `X-Registry-Auth` for the Docker API pull request targeting the requested registry.\n\n## Reproduction\n\nUse the attached/local proof:\n\n```sh\nnode submissions/github-gsa/cdxgen-docker-registry-auth-substring-forwarding/evidence/cdxgen_docker_registry_auth_substring_probe.mjs\n```\n\nThe proof is fully local. It creates a temporary Docker config containing credentials for `private-registry.example.com`, starts a localhost mock Docker API endpoint, sets `DOCKER_HOST` to that endpoint, then calls cdxgen\u0027s exported Docker request path for a pull from `registry.example.com`.\n\nObserved vulnerable output:\n\n```json\n{\n  \"decision\": \"GO\",\n  \"dockerConfigAuthHost\": \"private-registry.example.com\",\n  \"requestedRegistry\": \"registry.example.com\",\n  \"substringMatch\": true,\n  \"dockerApiUrl\": \"/images/create?fromImage=registry.example.com/team/app:latest\",\n  \"headerPresent\": true,\n  \"decodedHeader\": {\n    \"username\": \"trusted-user\",\n    \"password\": \"trusted-pass\",\n    \"serveraddress\": \"private-registry.example.com\"\n  }\n}\n```\n\n## Impact\n\nIf an operator has Docker credentials for a private registry and uses cdxgen to scan an image from a different registry whose hostname is a substring of that private registry hostname, cdxgen can attach the private registry credentials to the Docker pull request for the different registry.\n\nIn a realistic attack, an attacker who controls or can observe the requested registry can induce a victim to scan an image from that registry. The Docker daemon API receives an `X-Registry-Auth` payload containing credentials for the victim\u0027s private registry but associated with the attacker-requested pull. This is a credential forwarding/misbinding issue in cdxgen\u0027s container image handling.\n\n\n## References\n\nFunctions `normalizeRegistryHost` and `registriesMatch` added to normalize and perform strict host matching.\n\nFix PR: https://github.com/cdxgen/cdxgen/pull/3964\n\nResearcher: Francesco SabiuResearcher: Francesco Sabiu",
  "id": "GHSA-qhh4-458h-xwh2",
  "modified": "2026-05-08T20:06:00Z",
  "published": "2026-05-08T20:06:00Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/cdxgen/cdxgen/security/advisories/GHSA-qhh4-458h-xwh2"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/cdxgen/cdxgen"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:L/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "@cyclonedx/cdxgen: Docker registry auth substring match forwards credentials to a different registry"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…