Skip to main content

Introducing open weights model Apex Flash-1

All articles

Vulnerability Research

Apex Found Two Authentication Bypasses in wolfSSL

Apex found two configuration-specific authentication bypasses in wolfSSL. Both were fixed in version 5.9.4.

Cantina Security Research 7 min read
wolfSSL Authentication bypass Certificate validation TLS Apex
On this page
Apex found two authentication bypasses in wolfSSL. Two certificate panels illustrate a break in certificate validation.

Apex, Cantina’s autonomous pentester, found two bugs in wolfSSL that could let an application connect to an attacker’s server and accept it as legitimate. Both authentication bypasses affected specific configurations and were fixed in wolfSSL 5.9.4.

One bug could leave an unverified certificate issuer available to later checks after verification failed, while the other let a server choose a raw public key instead of a certificate chain without the client agreeing to that option.

Technical summary

CVE-2026-89136 CVE-2026-89135
Severity High · CVSS 4.0: 8.3 Medium · CVSS 4.0: 6.3
Vulnerability class Improper authentication (CWE-287) Improper certificate validation (CWE-295)
Affected component Raw Public Key negotiation in TLSX_ServerCertificateType_Parse() OpenSSL-compatible wolfSSL_X509_verify_cert() and the shared CertManager
Impact A server could select RawPublicKey without the client offering it and bypass expected authentication. A failed certificate check could leave an unverified CA loaded, allowing later checks through the same certificate manager to trust it.
Affected versions 5.6.0–5.9.2 5.8.4–5.9.2
Required conditions Client uses TLS 1.2, TLS 1.3 or DTLS 1.2 with HAVE_RPK enabled, including --enable-rpk, --enable-all or --enable-distro. RPK is off by default. OpenSSL compatibility enabled, such as --enable-opensslextra, and the application calls X509_verify_cert(). Later operations reuse the same certificate manager.
Fixed version 5.9.4 5.9.4
Fix Reject certificate-type responses the client never offered. Always attempt to remove temporary intermediate CAs on exit and report failure if cleanup fails.

Both fixes are included in PR #11009 and wolfSSL 5.9.4.

How wolfSSL checks identity

wolfSSL is responsible for managing TLS connections in applications; in most cases, the client verifies the server’s certificate by working through a chain of certificate issuers until it reaches a root certificate that the application trusts, the intermediate certificates serving to link the server’s certificate to that trusted root.

A raw public key (RPK) contains nothing but the public key and has no certificate chain; as stated in RFC 7250, the application must have some other means of verifying that the key belongs to the correct server, and the client and server also have to agree to use this format.

The certificate manager in wolfSSL records the issuers that are used when carrying out verification. When constructing a certificate chain it may be necessary to load additional issuers for a brief period. An issuer that is added for one check should not remain trusted merely because it was loaded for that check.

A server could choose a certificate type the client never offered

CVE-2026-89136, High, CVSS 4.0: 8.3

The server must use a certificate type the client accepts. A malicious server could respond with server_cert_type=RawPublicKey even if the client had not included that option. According to the wolfSSL advisory, accepting this response could allow authentication to be bypassed in clients where RPK support has been turned on.

In the function TLSX_ServerCertificateType_Parse(), the client verified the length of the response and stored the server’s choice without first checking that the client had offered that type.

// src/tls.c:13664-13665, wolfSSL 5.9.2
ssl->options.rpkState.received_ServerCertTypeCnt  = 1;
ssl->options.rpkState.received_ServerCertTypes[0] = *input;

A further function, ProcessPeerCertParse(), verified whether the format of the received certificate matched the agreed type. On the client side, it got that expected type from the value which had just been provided by the server:

// src/internal.c:15701-15705, wolfSSL 5.9.2
cType = WOLFSSL_CERT_TYPE_X509;
if (ssl->options.side == WOLFSSL_CLIENT_END) {
    if (ssl->options.rpkState.received_ServerCertTypeCnt == 1)
        cType = ssl->options.rpkState.received_ServerCertTypes[0];
}

That meant that the server could choose RawPublicKey and then send a raw key; the format check would be passed even if the client had never agreed to use that format. As raw-key parsing doesn’t verify a certificate chain, accepting the server’s choice allowed it to bypass the certificate-based authentication that the client had expected.

Now the fixed parser verifies what the client has offered before saving the server’s response; if the client did not include the extension or if the server chose a type that is not on the list, it returns UNSUPPORTED_EXTENSION. The patch also includes these checks in client_cert_type.

The TLS 1.3 regression test that has been published by wolfSSL simulates an unsolicited response; when peer verification is enabled, the test initiates a handshake, then deletes the client’s recorded offer before the client has a chance to process the server’s answer. The test asserts that the client rejects the response with UNSUPPORTED_EXTENSION and carries out this check for both server_cert_type and client_cert_type.

A failed check could leave an unverified issuer trusted

CVE-2026-89135, Medium, CVSS 4.0: 6.3

Even if the first certificate check had failed, an attacker could place an unverified certificate issuer into wolfSSL’s shared certificate manager. Subsequent checks could then end up trusting that issuer, and the CVE record describes this issue in the OpenSSL-compatible X509_verify_cert() API.

When constructing a certificate chain, wolfSSL_X509_verify_cert() temporarily added the supplied issuer using X509StoreAddCa(..., WOLFSSL_TEMP_CA). This enabled it to verify the signature of the child certificate, but it did not mean that the application trusted the issuer.

The verifier set the added flag to 1 to record the temporary addition; if the signature check had failed early in the chain, then this failure route could have cleared the flag without removing the issuer:

// src/x509_str.c:778-786, wolfSSL 5.9.2
added = 1;
ret = X509StoreVerifyCert(ctx);
if (ret != WOLFSSL_SUCCESS) {
    if ((origDepth - depth) <= 1)
        added = 0;
    X509VerifyCertSetupRetry(ctx, certs, failedCerts,
        &depth, origDepth);
    continue;
}

Later, cleanup only removed temporary issuers when that flag was set:

// src/x509_str.c:919-921, wolfSSL 5.9.2
if (added == 1) {
    wolfSSL_CertManagerUnloadTempIntermediateCerts(ctx->store->cm);
}

The function might fail yet still leave the issuer in the certificate manager; other checks that use the same manager, such as native TLS, OCSP, CRL, and direct certificate verification, could then detect that issuer and would not be able to determine whether it was a temporary one or a trusted one. A rejection of this kind could affect which certificates are later accepted by the checks.

The fix makes the verifier attempt to remove temporary issuers whenever it exits and report failure if cleanup fails. The major change in the patch is:

// src/x509_str.c:1271-1278 in wolfSSL 5.9.4; logging omitted
- if (added == 1) {
-     wolfSSL_CertManagerUnloadTempIntermediateCerts(ctx->store->cm);
+ if (wolfSSL_CertManagerUnloadTempIntermediateCerts(ctx->store->cm)
+         != WOLFSSL_SUCCESS) {
+     if (ret == WOLFSSL_SUCCESS)
+         ret = WOLFSSL_FAILURE;
  }

The regression test that wolfSSL has published checks the result that remains after a failure; it begins with a store that trusts only a root certificate and provides the intermediate certificate separately as part of the verification attempt.

  1. The test alters one bit in the signature of the end certificate, which causes the signature to become invalid.
  2. It hands the damaged certificate together with its intermediate certificate issuer to the function X509_verify_cert() and then asserts that the verification fails.
  3. The original, undamaged certificate is passed directly to the function wolfSSL_CertManagerVerifyBuffer() using the same certificate manager, without the intermediate being provided again.
  4. It asserts that the second check too fails.

The certificate which has not been damaged still requires its intermediate certificate issuer to establish a chain to the trusted root. That issuer should have been deleted after the first attempt. If the second certificate check succeeded, that would mean the first attempt had left the intermediate certificate issuer behind.

What affected teams should update

To update your application or device’s wolfSSL library to a version that includes both fixes, start with 5.9.4 or obtain the appropriate backport from your vendor. Make sure to check the version and build options in your application, not merely the version of the library available system-wide.

For CVE-2026-89135, the vendor states that the build must use OPENSSL_EXTRA and at the same time not use NO_CERTS and not use WOLFCRYPT_ONLY, or it must be a build that has --enable-opensslextra. In either case, X509_verify_cert() must be used by the application. It is necessary for the first failed call to leave an issuer in a certificate manager that is used by the subsequent checks; merely using native TLS is not sufficient to meet that requirement.

Disclosure and fixes

  1. On July 30, 2026 wolfSSL submitted PR #11009 which included the fixes for the issues that had been reported.
  2. On August 11, 2026, the project incorporated that pull request.
  3. In September 2026, wolfSSL incorporated both fixes into version 5.9.4, a version whose release notes were issued on September 25; the CVE records were made public on September 27.

The two vulnerabilities were found by Apex, and the wolfSSL team carried out the fixes and released them.

What these findings mean for testing

A failed certificate check should leave no issuer behind that later checks could trust, and a server’s choice should only be accepted if the client offered it.