VYPR
High severity8.0NVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-3012

CVE-2026-3012

Description

A flaw was found in Samba’s certificate auto-enrollment Group Policy handling. When certificate auto-enrollment is enabled, Samba may retrieve a CA certificate over an unencrypted HTTP connection and install it into the local trust store without proper verification. An attacker with the ability to intercept or redirect network traffic could exploit this behavior to supply a malicious certificate authority certificate, potentially allowing interception or spoofing of trusted communications.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Samba's certificate auto-enrollment Group Policy handler retrieves CA certificates over unencrypted HTTP without validation, allowing attackers to inject malicious CA certificates.

Vulnerability

A flaw exists in Samba's Group Policy certificate auto-enrollment feature. When enabled, Samba retrieves a CA certificate over an unencrypted HTTP connection and installs it into the local trust store without proper verification. This affects Samba implementations that enable certificate auto-enrollment via Group Policy [1][2].

Exploitation

An attacker with the ability to intercept or redirect network traffic (e.g., via a man-in-the-middle attack) can exploit this behavior. The attacker intercepts the HTTP request for the CA certificate and responds with a malicious CA certificate. Samba then installs this certificate into the trust store without validation [1][2].

Impact

Successful exploitation allows the attacker to supply a malicious certificate authority certificate that becomes trusted by the system. This enables interception or spoofing of trusted communications, potentially compromising the confidentiality and integrity of encrypted sessions [1][2].

Mitigation

As of the publication date (2026-05-27), no official fix has been released. Administrators should disable certificate auto-enrollment if not required, or ensure that network traffic for certificate retrieval is secured (e.g., by using HTTPS or enforcing certificate validation). Monitor Samba updates for a patched version [1][2].

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

4
935e5a9ecd38

CVE-2026-3012: gpo tests should use real certificates

https://github.com/samba-team/sambaDouglas BagnallFeb 27, 2026via github-commit-search
2 files changed · +4 5
  • python/samba/tests/gpo.py+4 4 modified
    @@ -7062,15 +7062,15 @@ def test_gp_cert_auto_enroll_ext(self):
             ldb.add({'dn': certa_dn,
                      'objectClass': 'certificationAuthority',
                      'authorityRevocationList': ['XXX'],
    -                 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
    +                 'cACertificate': dummy_certificate(),
                      'certificateRevocationList': ['XXX'],
                     })
             # Write the dummy pKIEnrollmentService
             enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
             self.addCleanup(ldb.delete, enroll_dn)
             ldb.add({'dn': enroll_dn,
                      'objectClass': 'pKIEnrollmentService',
    -                 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
    +                 'cACertificate': dummy_certificate(),
                      'certificateTemplates': ['Machine'],
                      'dNSHostName': hostname,
                     })
    @@ -7673,15 +7673,15 @@ def test_advanced_gp_cert_auto_enroll_ext(self):
             ldb.add({'dn': certa_dn,
                      'objectClass': 'certificationAuthority',
                      'authorityRevocationList': ['XXX'],
    -                 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
    +                 'cACertificate': dummy_certificate(),
                      'certificateRevocationList': ['XXX'],
                     })
             # Write the dummy pKIEnrollmentService
             enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
             self.addCleanup(ldb.delete, enroll_dn)
             ldb.add({'dn': enroll_dn,
                      'objectClass': 'pKIEnrollmentService',
    -                 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
    +                 'cACertificate': dummy_certificate(),
                      'certificateTemplates': ['Machine'],
                      'dNSHostName': hostname,
                     })
    
  • selftest/knownfail.d/gpo-auto-enrol+0 1 modified
    @@ -1,2 +1 @@
     ^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_advanced_gp_cert_auto_enroll_ext\(ad_dc:local\)
    -^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_gp_cert_auto_enroll_ext\(ad_dc:local\)
    
c03e7dcf5113

CVE-2026-3012: gpo tests: fix test cleanup

https://github.com/samba-team/sambaDouglas BagnallFeb 26, 2026via github-commit-search
1 file changed · +25 17
  • python/samba/tests/gpo.py+25 17 modified
    @@ -6951,6 +6951,7 @@ def test_gp_cert_auto_enroll_ext_without_ndes(self):
             confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
             ca_cn = '%s-CA' % hostname.replace('.', '-')
             certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
    +        self.addCleanup(ldb.delete, certa_dn)
             ldb.add({'dn': certa_dn,
                      'objectClass': 'certificationAuthority',
                      'authorityRevocationList': ['XXX'],
    @@ -6959,6 +6960,7 @@ def test_gp_cert_auto_enroll_ext_without_ndes(self):
                     })
             # Write the dummy pKIEnrollmentService
             enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
    +        self.addCleanup(ldb.delete, enroll_dn)
             ldb.add({'dn': enroll_dn,
                      'objectClass': 'pKIEnrollmentService',
                      'cACertificate': dummy_certificate(),
    @@ -6967,6 +6969,7 @@ def test_gp_cert_auto_enroll_ext_without_ndes(self):
                     })
             # Write the dummy pKICertificateTemplate
             template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
    +        self.addCleanup(ldb.delete, template_dn)
             ldb.add({'dn': template_dn,
                      'objectClass': 'pKICertificateTemplate',
                     })
    @@ -7012,11 +7015,6 @@ def test_gp_cert_auto_enroll_ext_without_ndes(self):
                 self.assertNotIn(b'Workstation', out,
                                  'Workstation certificate not removed')
     
    -        # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
    -        ldb.delete(certa_dn)
    -        ldb.delete(enroll_dn)
    -        ldb.delete(template_dn)
    -
             # Unstage the Registry.pol file
             unstage_file(reg_pol)
     
    @@ -7027,6 +7025,7 @@ def test_gp_cert_auto_enroll_ext(self):
                                    'MACHINE/REGISTRY.POL')
             cache_dir = self.lp.get('cache directory')
             store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
    +        self.addCleanup(store.log.close)
     
             machine_creds = Credentials()
             machine_creds.guess(self.lp)
    @@ -7059,6 +7058,7 @@ def test_gp_cert_auto_enroll_ext(self):
             confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
             ca_cn = '%s-CA' % hostname.replace('.', '-')
             certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
    +        self.addCleanup(ldb.delete, certa_dn)
             ldb.add({'dn': certa_dn,
                      'objectClass': 'certificationAuthority',
                      'authorityRevocationList': ['XXX'],
    @@ -7067,6 +7067,7 @@ def test_gp_cert_auto_enroll_ext(self):
                     })
             # Write the dummy pKIEnrollmentService
             enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
    +        self.addCleanup(ldb.delete, enroll_dn)
             ldb.add({'dn': enroll_dn,
                      'objectClass': 'pKIEnrollmentService',
                      'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
    @@ -7075,12 +7076,16 @@ def test_gp_cert_auto_enroll_ext(self):
                     })
             # Write the dummy pKICertificateTemplate
             template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
    +        self.addCleanup(ldb.delete, template_dn)
             ldb.add({'dn': template_dn,
                      'objectClass': 'pKICertificateTemplate',
                     })
     
             with TemporaryDirectory() as dname:
    -            ext.process_group_policy([], gpos, dname, dname)
    +            try:
    +                ext.process_group_policy([], gpos, dname, dname)
    +            except Exception as e:
    +                self.fail(f"process_group_policy() raised {e}")
                 ca_crt = os.path.join(dname, '%s.crt' % ca_cn)
                 self.assertTrue(os.path.exists(ca_crt),
                                 'Root CA certificate was not requested')
    @@ -7169,11 +7174,6 @@ def test_gp_cert_auto_enroll_ext(self):
                 self.assertNotIn(b'Workstation', out,
                                  'Workstation certificate not removed')
     
    -        # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
    -        ldb.delete(certa_dn)
    -        ldb.delete(enroll_dn)
    -        ldb.delete(template_dn)
    -
             # Unstage the Registry.pol file
             unstage_file(reg_pol)
     
    @@ -7626,6 +7626,7 @@ def test_advanced_gp_cert_auto_enroll_ext(self):
                                    'MACHINE/REGISTRY.POL')
             cache_dir = self.lp.get('cache directory')
             store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
    +        self.addCleanup(store.log.close)
     
             machine_creds = Credentials()
             machine_creds.guess(self.lp)
    @@ -7667,6 +7668,8 @@ def test_advanced_gp_cert_auto_enroll_ext(self):
             confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
             ca_cn = '%s-CA' % hostname.replace('.', '-')
             certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
    +        self.addCleanup(ldb.delete, certa_dn)
    +
             ldb.add({'dn': certa_dn,
                      'objectClass': 'certificationAuthority',
                      'authorityRevocationList': ['XXX'],
    @@ -7675,6 +7678,7 @@ def test_advanced_gp_cert_auto_enroll_ext(self):
                     })
             # Write the dummy pKIEnrollmentService
             enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
    +        self.addCleanup(ldb.delete, enroll_dn)
             ldb.add({'dn': enroll_dn,
                      'objectClass': 'pKIEnrollmentService',
                      'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
    @@ -7683,12 +7687,21 @@ def test_advanced_gp_cert_auto_enroll_ext(self):
                     })
             # Write the dummy pKICertificateTemplate
             template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
    +        try:
    +            ldb.delete(template_dn)
    +        except _ldb.LdbError:
    +            pass
    +
    +        self.addCleanup(ldb.delete, template_dn)
             ldb.add({'dn': template_dn,
                      'objectClass': 'pKICertificateTemplate',
                     })
     
             with TemporaryDirectory() as dname:
    -            ext.process_group_policy([], gpos, dname, dname)
    +            try:
    +                ext.process_group_policy([], gpos, dname, dname)
    +            except Exception as e:
    +                self.fail(f"process_group_policy() raised {e}")
                 ca_list = [ca_cn, 'example0-com-CA', 'example1-com-CA',
                            'example2-com-CA']
                 for ca in ca_list:
    @@ -7751,11 +7764,6 @@ def test_advanced_gp_cert_auto_enroll_ext(self):
                 self.assertNotIn(b'Workstation', out,
                                  'Workstation certificate not removed')
     
    -        # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
    -        ldb.delete(certa_dn)
    -        ldb.delete(enroll_dn)
    -        ldb.delete(template_dn)
    -
             # Unstage the Registry.pol file
             unstage_file(reg_pol)
     
    
160e83193308

CVE-2026-3012: gp_auto_enrol: skip CAs not found in LDAP

https://github.com/samba-team/sambaDouglas BagnallFeb 26, 2026via github-commit-search
1 file changed · +10 0
  • python/samba/gp/gp_cert_auto_enroll_ext.py+10 0 modified
    @@ -452,11 +452,21 @@ def __read_cep_data(self, guid, ldb, end_point_information,
                         # This is a basic configuration.
                         cas = fetch_certification_authorities(ldb)
                         for _ca in cas:
    +                        if 'cACertificate' not in _ca:
    +                            log.warning(f"ignoring CA '{_ca['name']}' with no "
    +                                        "cACertificate in LDAP.")
    +                            continue
    +
                             self.apply(guid, _ca, cert_enroll, _ca, ldb, trust_dir,
                                        private_dir)
                             ca_names.append(_ca['name'])
                     # If EndPoint.URI starts with "HTTPS//":
                     elif ca['URL'].lower().startswith('https://'):
    +                    if 'cACertificate' not in ca:
    +                        log.warning(f"ignoring CA '{ca['name']}' "
    +                                    f"({ca['URL']}) with no "
    +                                    "cACertificate in LDAP.")
    +                        continue
                         self.apply(guid, ca, cert_enroll, ca, ldb, trust_dir,
                                    private_dir, auth=ca['auth'])
                         ca_names.append(ca['name'])
    
4c2db6489be1

CVE-2026-3012: do not fetch certificate over http

https://github.com/samba-team/sambaDouglas BagnallFeb 22, 2026via github-commit-search
2 files changed · +11 45
  • python/samba/gp/gp_cert_auto_enroll_ext.py+9 45 modified
    @@ -16,7 +16,6 @@
     
     import os
     import operator
    -import requests
     from samba.gp.gpclass import gp_pol_ext, gp_applier, GPOSTATE
     from samba import Ldb
     from samba.dcerpc import misc
    @@ -195,58 +194,24 @@ def get_supported_templates(server):
         return out.strip().split()
     
     
    -def getca(ca, url, trust_dir):
    -    """Fetch Certificate Chain from the CA."""
    +def getca(ca, trust_dir):
    +    """Fetch a certificate from LDAP."""
         root_cert = os.path.join(trust_dir, '%s.crt' % ca['name'])
         root_certs = []
    -
    -    try:
    -        r = requests.get(url=url, params={'operation': 'GetCACert',
    -                                          'message': 'CAIdentifier'})
    -    except requests.exceptions.ConnectionError:
    -        log.warn('Could not connect to Network Device Enrollment Service.')
    -        r = None
    -    if r is None or r.content == b'' or r.headers['Content-Type'] == 'text/html':
    -        log.warn('Unable to fetch root certificates (requires NDES).')
    -        if 'cACertificate' in ca:
    -            log.warn('Installing the server certificate only.')
    -            der_certificate = base64.b64decode(ca['cACertificate'])
    -            try:
    -                cert = load_der_x509_certificate(der_certificate)
    -            except TypeError:
    -                cert = load_der_x509_certificate(der_certificate,
    -                                                 default_backend())
    -            cert_data = cert.public_bytes(Encoding.PEM)
    -            with open(root_cert, 'wb') as w:
    -                w.write(cert_data)
    -            root_certs.append(root_cert)
    -        return root_certs
    -
    -    if r.headers['Content-Type'] == 'application/x-x509-ca-cert':
    -        # Older versions of load_der_x509_certificate require a backend param
    +    if 'cACertificate' in ca:
    +        log.warn('Installing the server certificate only.')
    +        der_certificate = base64.b64decode(ca['cACertificate'])
             try:
    -            cert = load_der_x509_certificate(r.content)
    +            cert = load_der_x509_certificate(der_certificate)
             except TypeError:
    -            cert = load_der_x509_certificate(r.content, default_backend())
    +            cert = load_der_x509_certificate(der_certificate,
    +                                             default_backend())
             cert_data = cert.public_bytes(Encoding.PEM)
             with open(root_cert, 'wb') as w:
                 w.write(cert_data)
             root_certs.append(root_cert)
    -    elif r.headers['Content-Type'] == 'application/x-x509-ca-ra-cert':
    -        certs = load_der_pkcs7_certificates(r.content)
    -        for i in range(0, len(certs)):
    -            cert = certs[i].public_bytes(Encoding.PEM)
    -            filename, extension = root_cert.rsplit('.', 1)
    -            dest = '%s.%d.%s' % (filename, i, extension)
    -            with open(dest, 'wb') as w:
    -                w.write(cert)
    -            root_certs.append(dest)
    -    else:
    -        log.warn('getca: Wrong (or missing) MIME content type')
    -
         return root_certs
     
    -
     def find_global_trust_dir():
         """Return the global trust dir using known paths from various Linux distros."""
         for trust_dir in global_trust_dirs:
    @@ -266,11 +231,10 @@ def changed(new_data, old_data):
     def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
         """Install the root certificate chain."""
         data = dict({'files': [], 'templates': []}, **ca)
    -    url = 'http://%s/CertSrv/mscep/mscep.dll/pkiclient.exe?' % ca['hostname']
     
         log.info("Try to get root or server certificates")
     
    -    root_certs = getca(ca, url, trust_dir)
    +    root_certs = getca(ca, trust_dir)
         data['files'].extend(root_certs)
         global_trust_dir = find_global_trust_dir()
         for src in root_certs:
    
  • selftest/knownfail.d/gpo-auto-enrol+2 0 added
    @@ -0,0 +1,2 @@
    +^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_advanced_gp_cert_auto_enroll_ext\(ad_dc:local\)
    +^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_gp_cert_auto_enroll_ext\(ad_dc:local\)
    

Vulnerability mechanics

Root cause

"Samba's certificate auto-enrollment fetched a CA certificate over an unencrypted HTTP connection and installed it into the local trust store without any verification."

Attack vector

An attacker with network access who can intercept or redirect HTTP traffic (adjacent network, CVSS AV:A) can exploit Samba's certificate auto-enrollment Group Policy handling. When certificate auto-enrollment is enabled, Samba's `getca()` function issued an unencrypted HTTP GET request to the NDES-like endpoint to retrieve a CA certificate [patch_id=2654243]. The response was trusted without any verification and installed into the local trust store. An attacker who can perform a man-in-the-middle attack on this HTTP connection can supply a malicious CA certificate, enabling interception or spoofing of trusted communications.

Affected code

The vulnerability is in `python/samba/gp/gp_cert_auto_enroll_ext.py`. The `getca()` function (lines 197–254 before the patch) fetched a CA certificate over plain HTTP from `http://%s/CertSrv/mscep/mscep.dll/pkiclient.exe?` [patch_id=2654243]. The `cert_enroll()` function constructed this URL and called `getca()` with it. The patches also modify `python/samba/tests/gpo.py` to use parseable certificates and improve test cleanup [patch_id=2654242][patch_id=2654241].

What the fix does

Patch [patch_id=2654243] removes the entire HTTP fetch path from `getca()`. The function no longer makes a `requests.get()` call to an HTTP URL; instead it only reads the CA certificate from the `cACertificate` attribute already obtained via LDAP. The commit message explains that there was no way to verify the HTTP-derived certificate other than comparing it to LDAP-derived certificates, and LDAP was already the fallback — so the patch makes LDAP the only path. Patch [patch_id=2654240] adds a guard in `__read_cep_data()` to skip CAs that lack a `cACertificate` attribute in LDAP, preventing attempts to fetch them via any other channel.

Preconditions

  • configCertificate auto-enrollment Group Policy must be enabled on the Samba domain controller
  • networkAttacker must be on the same adjacent network (CVSS AV:A) to intercept or redirect HTTP traffic
  • authNo authentication required (CVSS PR:N) — the HTTP fetch is unauthenticated

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.