CVE-2026-56821: Netty · Out-of-date OCSP Responses Accepted by OcspServerCertificateValidator
Affects io.netty:netty-handler-ssl-ocspSource GCVE Database
Not known to be exploited · CVSS 7.4 High : upgrade to 4.2.16.Final soon.
Data as of 26 Oct 2026, 17:16 UTC
- EPSS
- 0.2% chance of exploitation in 30 days – higher than 12% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
- Patch
- Available – fixed in 4.2.16.Final, 4.1.136.Final
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:NExploitation evidence
Disclosed: evidence found.
CVE published 22 Jul 2026
Detectable: no evidence on record.
No public nuclei template indexed
Weaponised: no evidence on record.
No public exploit code indexed in Metasploit or Exploit-DB
Exploited in the wildExploited: no evidence on record.
Not in CISA KEV and no source reports exploitation
Observed by OffSeq MirageSeen by Mirage: no evidence on record.
No attack traffic recorded by OffSeq Mirage sensors
A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.
What to do
Upgrade io.netty:netty-handler-ssl-ocsp to 4.2.16.Final; io.netty:netty-handler-ssl-ocsp to 4.1.136.Final.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
| Mavenio.netty:netty-handler-ssl-ocsp | Affected
| Fixed in
|
| Mavenio.netty:netty-handler-ssl-ocsp | Affected
| Fixed in
|
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
Description
### Summary `OcspServerCertificateValidator` flags an out-of-date OCSP response but does not stop processing it, so an expired GOOD response is still reported as `VALID`, letting an on-path attacker replay a stale GOOD response to bypass revocation of a since-revoked certificate. ### Details In `io.netty.handler.ssl.ocsp.OcspServerCertificateValidator#userEventTriggered` the freshness check has no `return`, so execution falls through and a `VALID` `OcspValidationEvent` is still fired: ```java if (!(current.after(response.getThisUpdate()) && current.before(response.getNextUpdate()))) { ctx.fireExceptionCaught(new IllegalStateException("OCSP Response is out-of-date")); } ``` Nonce validation is optional and off by default, so freshness is the only replay defense — and it is not enforced. Additionally `getNextUpdate()` may be `null`, making `current.before(null)` throw `NullPointerException`. https://datatracker.ietf.org/doc/html/rfc6960#section-3.2 ``` 5. The time at which the status being indicated is known to be correct (thisUpdate) is sufficiently recent; 6. When available, the time at or before which newer information will be available about the status of the certificate (nextUpdate) is greater than the current time. ``` ### PoC Add the test below to `io.netty.handler.ssl.ocsp.OcspServerCertificateValidatorTest` ```java @Test void staleOcspResponseIsRejected() throws Exception { X509Bundle caRoot = new CertificateBuilder() .algorithm(CertificateBuilder.Algorithm.rsa2048) .subject("CN=TrustedRootCA") .setIsCertificateAuthority(true) .buildSelfSigned(); GeneralName ocspName = new GeneralName(GeneralName.uniformResourceIdentifier, "http://localhost/"); AuthorityInformationAccess aia = new AuthorityInformationAccess( new AccessDescription(AccessDescription.id_ad_ocsp, ocspName)); X509Bundle targetCert = new CertificateBuilder() .algorithm(CertificateBuilder.Algorithm.rsa2048) .subject("CN=TargetServer") .addExtensionOctetString("1.3.6.1.5.5.7.1.1", false, aia.getEncoded()) .buildIssuedBy(caRoot); Date past = new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)); CertificateID certId = new CertificateID( new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1), new JcaX509CertificateHolder(caRoot.getCertificate()), targetCert.getCertificate().getSerialNumber()); BasicOCSPRespBuilder respBuilder = new BasicOCSPRespBuilder( new RespID(new JcaX509CertificateHolder(caRoot.getCertificate()).getSubject())); respBuilder.addResponse(certId, CertificateStatus.GOOD, past, past); BasicOCSPResp expiredBasicResp = respBuilder.build( new JcaContentSignerBuilder("SHA256withRSA").build(caRoot.getKeyPair().getPrivate()), new X509CertificateHolder[0], past); final byte[] responseEncoded = new OCSPRespBuilder() .build(OCSPRespBuilder.SUCCESSFUL, expiredBasicResp).getEncoded(); IoTransport defaultTransport = createDefaultTransport(); IoTransport mockTransport = IoTransport.create(defaultTransport.eventLoop(), () -> { NioSocketChannel channel = new NioSocketChannel(); channel.pipeline().addFirst(new ChannelOutboundHandlerAdapter() { @Override public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { promise.setSuccess(); ctx.executor().execute(() -> { ctx.pipeline().fireChannelActive(); DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(responseEncoded)); httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/ocsp-response"); httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, httpResponse.content().readableBytes()); ctx.pipeline().fireChannelRead(httpResponse); }); } }); return channel; }, defaultTransport.datagramChannel()); SslContext serverSslCtx = SslContextBuilder .forServer(targetCert.getKeyPair().getPrivate(), targetCert.getCertificate(), caRoot.getCertificate