# Apex Found Two Authentication Bypasses in wolfSSL > Apex found two configuration-specific authentication bypasses in wolfSSL. Both were fixed in version 5.9.4. Author: Cantina Security Research Published: October 20, 2026 Topics: wolfSSL, Authentication bypass, Certificate validation, TLS, Apex Canonical URL: https://www.cantina.security/blog/wolfssl-authentication-bypass [Apex, Cantina’s autonomous pentester](https://www.cantina.security/apex), found two bugs in [wolfSSL](https://www.wolfssl.com/) 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](https://github.com/wolfSSL/wolfssl/releases/tag/v5.9.4-stable). 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](https://www.cve.org/CVERecord?id=CVE-2026-89136) | [CVE-2026-89135](https://www.cve.org/CVERecord?id=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](https://github.com/wolfSSL/wolfssl/pull/11009) and [wolfSSL 5.9.4](https://github.com/wolfSSL/wolfssl/releases/tag/v5.9.4-stable). ## 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](https://www.rfc-editor.org/rfc/rfc7250.html#section-6), 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](https://www.wolfssl.com/docs/security-vulnerabilities/), accepting this response could allow authentication to be bypassed in clients where RPK support has been turned on. In the function [`TLSX_ServerCertificateType_Parse()`](https://github.com/wolfSSL/wolfssl/blob/v5.9.2-stable/src/tls.c#L13659-L13666), the client verified the length of the response and stored the server's choice without first checking that the client had offered that type. ```c // 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()`](https://github.com/wolfSSL/wolfssl/blob/v5.9.2-stable/src/internal.c#L15692-L15718), 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: ```c // 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](https://github.com/wolfSSL/wolfssl/blob/v5.9.4-stable/src/tls.c#L13957-L13978) 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](https://github.com/wolfSSL/wolfssl/blob/v5.9.4-stable/tests/api/test_tls13.c#L5135-L5190) 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](https://www.cve.org/CVERecord?id=CVE-2026-89135) 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](https://github.com/wolfSSL/wolfssl/blob/v5.9.2-stable/src/x509_str.c#L778-L786) could have cleared the flag without removing the issuer: ```c // 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](https://github.com/wolfSSL/wolfssl/blob/v5.9.2-stable/src/x509_str.c#L916-L923): ```c // 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](https://github.com/wolfSSL/wolfssl/pull/11009/files) is: ```diff // 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](https://github.com/wolfSSL/wolfssl/blob/v5.9.4-stable/tests/api/test_ossl_x509_str.c#L2074-L2127) 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](https://github.com/wolfSSL/wolfssl/pull/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](https://github.com/wolfSSL/wolfssl/releases/tag/v5.9.4-stable), 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.