Skip to content

OCSP Certificate Verification Check

Moderate
randombit published GHSA-4v9w-qvcq-6q7w Nov 16, 2022

Package

No package listed

Affected versions

<= 2.19.2

Patched versions

2.19.3+

Description

Impact

OCSP responses for some end entity are either signed by the issuing CA certificate of the PKI, or an OCSP responder certificate that the PKI authorized to sign responses in their name. In the latter case, the responder certificate (and its validation path certificate) may be embedded into the OCSP response and clients must verify that such certificates are indeed authorized by the CA when validating OCSP responses.

Botan 2.19.2 and older failed to verify that an authorized responder certificate embedded in an OCSP response is authorized by the issuing CA. As a result, any valid signature by an embedded certificate passed the check and was allowed to make claims about the revocation status of certificates of any CA. This issue was introduced in Botan version 1.11.34 released in November 2016.

Attackers that are in a position to spoof OCSP responses for a client could therefore render legitimate certificates of a 3rd party CA as revoked or even use a compromised (and actually revoked) certificate by spoofing an OCSP-"OK" response. E.g. an attacker could exploit this to impersonate a legitimate TLS server using a compromised certificate of that host and get around the revocation check using OCSP stapling.

Patches

The issue is mitigated in Botan version 2.19.3 and will be fully addressed with Botan 3.0.0 as a comprehensive patch is simplified by the possibility to change user-facing APIs.

Workarounds

Users of the library can manually verify that OCSP responses were signed by the legitimate CA certificate that issued the certificate in question. This essentially forbids authorized certificates altogether. Many CAs don't utilize this delegation mechanism to sign their OCSP responses anyway.

class MyTlsCallbacks : public Botan::TLS::Callbacks {

void tls_verify_cert_chain(
            const std::vector<Botan::X509_Certificate>& cert_chain,
            const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
            const std::vector<Botan::Certificate_Store*>& trusted_roots,
            Botan::Usage_Type usage,
            const std::string& hostname,
            const Botan::TLS::Policy& policy) override {
    auto response_issued_by =
        [](const Botan::OCSP::Response* response, const Botan::X509_Certificate& ca) {
            return (!response->signer_name().empty() && ca.subject_dn() == response->signer_name()) ||
                    (!response->signer_key_hash().empty() && ca.subject_public_key_bitstring_sha1() == response->signer_key_hash());
        };

    auto potentially_affected_by_ocsp_vuln =
        [&](const std::shared_ptr<const Botan::OCSP::Response>& response, const Botan::X509_Certificate& ca) {
            // nullptr responses are obviously not affected
            if(!response)
                return false;

            // responses that were issued by the CA directly are not affected
            if(response_issued_by(response.get(), ca))
                return false;

            // otherwise we need to assume that the OCSP response cannot be trusted
            return true;
        };

    // remove OCSP responses that might be affected by the vulnerability         
    std::vector<std::shared_ptr<const Botan::OCSP::Response>> filtered_responses;
    for(size_t i = 0; i < cert_chain.size() - 1; ++i) {
        const Botan::X509_Certificate& ca = cert_chain.at(i+1);

        if(i < ocsp_responses.size()) {
            const auto& response = ocsp_responses.at(i);
            if(potentially_affected_by_ocsp_vuln(response, ca)) {
                filtered_responses.push_back(nullptr);
            } else {
                filtered_responses.push_back(response);
            }
        }
    }

    // do TLS certificate path validation without potentially malicious OCSP responses
    Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, filtered_responses, trusted_roots, usage, hostname, policy);
}

};

Users that do not rely on OCSP for certificate revocation checks are not affected. (Temporarily) switching to CRLs for revocation checks might be a workaround for some users.

For more information

If you have any questions or comments about this advisory:

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N

CVE ID

CVE-2022-43705

Weaknesses

No CWEs