Safeguard
Vulnerability Analysis•

OpenVPN CVE-2026-84790: How a NUL Byte in a Certificate Name Turns 'admin\0impersonator' Into 'admin'

OpenVPN 2.7.8 fixes CVE-2026-84790, a bug where a certificate whose common name contains a hidden NUL byte, such as admin\0impersonator, is treated as belonging to admin. A peer holding such a certificate could pass a --verify-x509-name admin check, and on a server it would be handed admin's per-client configuration.

Safeguard Security Team
Security Research
9 min read

OpenVPN 2.7.8 fixes CVE-2026-84790, a bug where a certificate whose common name contains a hidden NUL byte, such as admin\0impersonator, is treated as belonging to admin. A peer holding such a certificate could pass a --verify-x509-name admin check, and on a server it would be handed admin's per-client configuration. OpenVPN says versions from 2.3.0 onwards are affected. The current 2.6 release doesn't contain the fix.

This post is for anyone running OpenVPN servers or clients that make decisions based on certificate names. It covers how the bug works in the source, who can realistically exploit it, the gap for 2.6 users, a script to check whether your CA has ever issued such a certificate, and the mitigations that don't depend on names.

The short answer

CVE-2026-84790 (CVSS 4.0 score 7.6) affects OpenVPN 2.3.0 through 2.7.7 and is fixed in 2.7.8, released on 7 October 2026. Its CVE record was published on 13 October. The latest 2.6 release, 2.6.23, doesn't include the check. OpenVPN credits Vivek Parikh with the report. Exploiting it requires a certificate signed by a CA your OpenVPN trusts, with a NUL byte embedded in the subject field you match on. If your CA only issues certificates with subjects it chose itself, your exposure is low. If it signs whatever subject a request asks for, upgrade now. Either way, audit what it has already issued.

2.7.8 also fixes two more bugs that are triggered by hostile input from the other side of the connection:

CVEWhat it isAffectedFixed in
CVE-2026-84790NUL byte in certificate subject lets a peer match another identity2.3.0 to 2.7.7 (per OpenVPN)2.7.8
CVE-2026-84256 (published 7 Sep)Windows: crafted certificate subject leads to command execution through cmd.exe argument handling2.1_rc10 to 2.6.22, 2.7_alpha1 to 2.7.72.6.23 (partial), 2.7.8 (adds a second fix)
CVE-2026-88964Windows and Android clients: integer underflow on a malicious server's pushed DNS search domains, causing a crash or memory disclosure2.7_alpha3 to 2.7.72.7.8

One discrepancy to note: the CVE record for CVE-2026-84790 lists only 2.7_alpha1 through 2.7.7, while OpenVPN's announcement says 2.3.0 through 2.7.7. We compared the source and found the same code without the check in 2.6.23. Go by the announcement.

How a NUL byte becomes someone else's name

X.509 certificate fields are length-prefixed byte strings, so a common name can legally carry a \0 in the middle. C strings end at the first \0. Every bug of this kind lives in the gap between those two facts.

According to the fix commit, with the OpenSSL backend, OpenVPN already escaped embedded NULs when it printed the whole subject, but extract_x509_field_ssl() copied an individual field's bytes, NUL included, into a C buffer. In 2.7.7, that extracted field becomes the session's common name through backend_x509_get_username(), and it is formatted with %s, which stops at the NUL. Two consumers of that value matter most:

  • --verify-x509-name compares the common name with strcmp(). The commit message's own example is a certificate for admin\0impersonator being accepted under --verify-x509-name admin name.
  • --client-config-dir looks up the per-client file by the common name. So on a server, the impostor gets the real admin's file: its static VPN address, its routes and anything else pushed from there.

Here is the same truncation in a few lines of Python, using ctypes to give the bytes C-string semantics:

import ctypes
cn = "admin\x00impersonator".encode()      # the CN bytes inside the certificate
buf = ctypes.create_string_buffer(cn)       # copied into a C char buffer, NUL included
print(len(cn), "bytes in the certificate")
print("C string reads:", buf.value)         # what strcmp() and "%s" see
print("matches 'admin':", buf.value == b"admin")
18 bytes in the certificate
C string reads: b'admin'
matches 'admin': True

The fix makes x509_get_subject() refuse any certificate whose subject contains a NUL, and adds the same check where individual fields are extracted, for both the OpenSSL and Mbed TLS backends. Peers presenting such certificates now fail verification outright. The same commit also makes the Mbed TLS backend use the last matching field when a subject has two CNs, to match OpenSSL. If you build anything that keys access on a certificate name, that is worth knowing about.

We didn't run an end-to-end exploit against a live OpenVPN server. The behaviour above comes from the fix commit, the 2.7.7 source, and the commit's own unit tests.

Who can actually exploit this

The attacker needs a certificate your OpenVPN trusts. That limits the bug a lot, and it is why the advisory says "remote authenticated users". In practice the risk depends on how your CA decides what goes in a subject:

  • The CA signs the subject the requester asks for. Self-service enrollment portals, automated signing of certificate signing requests (CSRs), and scripts that pass a user-supplied name straight to the CA all fall here. Any user who can get a certificate can ask for admin\0something. This is the case to worry about.
  • The CA sets the subject itself, for example from a verified directory username that can't contain control characters. An attacker would first need to compromise the CA or the issuance process, and at that point a NUL byte is the least of your problems.
  • Clients checking the server's name. Many deployments sign clients and the server with one CA. Without --remote-cert-tls server, which requires the server extended key usage, a client certificate named vpn.example.com\0x could pass a client's --verify-x509-name vpn.example.com check and impersonate the server to other clients.

The 2.6 gap

As of 14 October 2026, OpenVPN's 2.6 branch ends at 2.6.23, released on 23 September 2026. Its changelog has nine security entries covering seven CVEs, including a first fix for CVE-2026-84256, but nothing for CVE-2026-84790. Its ssl_verify_openssl.c lacks both the NUL check in x509_get_subject() and the length check in extract_x509_field_ssl() that 2.7.8 added.

If you run 2.6, which many long-lived servers and distribution packages do, you have three options: move to 2.7.8, check whether your distribution has backported the fix to its 2.6 package, or rely on the mitigations below until a 2.6 release includes it.

Check whether your CA ever issued one

This is the most useful thing to do this week, whatever version you run. The script reads every PEM certificate under a directory, such as Easy-RSA's pki/issued, and flags subject fields containing NUL or other control characters:

#!/usr/bin/env python3
"""Flag certificates whose subject contains NUL or other control characters
(the CVE-2026-84790 pattern). Point it at your CA's issued-certificate store,
e.g. Easy-RSA's pki/issued. Needs: pip install cryptography
Usage: python3 cert_subject_audit.py <dir> [<dir> ...]"""
import pathlib, re, sys
from cryptography import x509

PEM = re.compile(rb"-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----", re.S)
BAD = re.compile(r"[\x00-\x1f\x7f]")
found = scanned = 0
for root in sys.argv[1:]:
    for path in pathlib.Path(root).rglob("*"):
        if path.suffix.lower() not in {".crt", ".pem", ".cer"} or not path.is_file():
            continue
        for block in PEM.findall(path.read_bytes()):
            scanned += 1
            cert = x509.load_pem_x509_certificate(block)
            for attr in cert.subject:
                if isinstance(attr.value, str) and BAD.search(attr.value):
                    found += 1
                    print(f"{path}: serial {cert.serial_number:x} {attr.oid._name}={attr.value!r}")
                    print(f"   C string sees {attr.oid._name}={attr.value.split(chr(0))[0]!r}")
print(f"\n{scanned} certificate(s) scanned, {found} suspicious subject field(s)")
sys.exit(1 if found else 0)

We tested it on a throwaway CA with three client certificates (admin, alice, and one issued with the CN admin\x00impersonator):

pki/issued/mallory.crt: serial 77ed250efa05a89396336414a5c3421d6f147064 commonName='admin\x00impersonator'
   C string sees commonName='admin'

3 certificate(s) scanned, 1 suspicious subject field(s)

openssl x509 -noout -subject also shows the byte, as /CN=admin\x00impersonator with the LibreSSL build we used. If you get a hit, revoke the certificate, publish the updated certificate revocation list, and check your OpenVPN logs for connections using that serial.

The script only sees certificates the CA kept copies of. If your CA doesn't archive what it signs, add the same check to the signing step: refuse any CSR whose subject contains a control character.

What to do

  1. Upgrade to 2.7.8 on servers and clients. That covers all three CVEs above.
  2. On 2.6, install 2.6.23 now, but don't treat it as complete. It has the first fix for CVE-2026-84256, while 2.7.8 adds a second one (stopping cmd.exe from expanding variables in quoted arguments) that isn't in any 2.6 release yet. Treat CVE-2026-84790, and on Windows part of CVE-2026-84256, as open until your package includes those fixes.
  3. Audit and fix issuance. Run the script against your CA's issued certificates, revoke anything it flags, and make the CA reject control characters in requested subjects.
  4. On clients, add --remote-cert-tls server. It requires the server's extended key usage, so a client certificate can't stand in for the server whatever its name says.
  5. For small setups, consider --peer-fingerprint. Available since 2.6.0, it pins the expected peer certificate by SHA-256 fingerprint instead of trusting a name. That sidesteps this whole class of bug.

The pattern behind it

Two of the three CVEs in this release are triggered by a crafted certificate subject. One turns it into a different identity, and on Windows the other turns it into a command. In both cases the subject was treated as text OpenVPN controlled, when it is data a CA copied, possibly from a user. The fix direction is the same in both: refuse what can't be represented safely, rather than trying to escape it after the fact.

To be fair to OpenVPN, the precondition here is real. Most deployments don't hand out certificates with user-chosen subjects, and the exploit can't start from outside your PKI. But identity checks based on string equality are a habit across VPNs, proxies and internal mTLS services. The same audit question applies to all of them: can a name contain a byte that your comparison doesn't see?

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.