HIGH poodle attackmutual tls

Poodle Attack with Mutual Tls

How POODLE Attack Manifests in Mutual TLS

The POODLE (Padding Oracle On Downgraded Legacy Encryption) attack, identified as CVE-2014-3566, exploits a flaw in the CBC-mode padding verification of SSL 3.0 and TLS 1.0. Although the attack is often described in the context of ordinary HTTPS, it also applies to any TLS session, including those that use mutual TLS (mTLS) for client authentication. The attack does not target the certificate exchange; it targets the record layer where encrypted application data is processed.

In an mTLS setup, both client and server present certificates during the handshake. If the server (or a middlebox) still permits SSLv3 or TLS 1.0 cipher suites, an active network attacker can force a protocol downgrade to one of those versions. Once the connection is using a vulnerable CBC cipher suite (e.g., TLS_RSA_WITH_AES_128_CBC_SHA), the attacker can repeatedly modify ciphertext blocks and observe whether the server’s padding validation fails or succeeds. By measuring the timing or error responses (such as a TLS alert vs. a closed connection), the attacker can gradually decrypt secret data—such as session cookies, authorization headers, or even the client certificate’s private key material if it is ever transmitted in the application data.

Because mTLS does not change the underlying record‑layer encryption, the presence of client certificates does not mitigate POODLE. The attack surface is the same: any server that accepts SSLv3/TLS 1.0 and uses CBC mode ciphers is vulnerable, regardless of whether it also requires a client certificate.

Mutual TLS‑Specific Detection

Detecting POODLE‑prone configurations in an mTLS service involves checking two independent factors:

  • Whether the server enables SSLv3 or TLS 1.0 protocol versions.
  • Whether any of the enabled cipher suites use CBC mode (as opposed to AEAD modes like GCM or ChaCha20-Poly1305).

middleBrick’s unauthenticated black‑box scan includes an "Encryption" check that probes the server’s TLS configuration. When you run a scan, the tool attempts a handshake with each supported version and cipher suite, then reports any weak or deprecated settings.

Example using the middleBrick CLI:

# Scan an mTLS‑protected API endpoint
middlebrick scan https://api.example.com/secure --output json

The resulting JSON may contain a finding such as:

{
  "id": "ENC-001",
  "name": "SSLv3/TLS 1.0 enabled",
  "severity": "high",
  "description": "The server accepts SSLv3 or TLS 1.0 protocol versions, which are vulnerable to the POODLE attack (CVE-2014-3566).",
  "remediation": "Disable SSLv3 and TLS 1.0; enforce TLS 1.2 or higher."
}

If the server also offers CBC ciphers, a second finding may appear:

{
  "id": "ENC-002",
  "name": "CBC mode cipher suites enabled",
  "severity": "medium",
  "description": "The server permits CBC‑mode cipher suites (e.g., TLS_RSA_WITH_AES_128_CBC_SHA). When combined with SSLv3/TLS 1.0, this enables POODLE.",
  "remediation": "Prefer AEAD cipher suites (TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256) and disable all CBC ciphers."
}

These findings give you the exact information needed to harden the mTLS configuration.

Mutual TLS‑Specific Remediation

Fixing POODLE in an mTLS service means removing the vulnerable protocol versions and cipher suites while preserving the mutual authentication flow. The changes are made in the TLS configuration of the server (or the load balancer/proxy that terminates TLS). Below are language‑specific examples that show how to enforce TLS 1.2+ and disable CBC mode.

Node.js (using the built‑in tls module)

const tls = require('tls');
const fs = require('fs');

const options = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt'),
  ca: fs.readFileSync('client-ca.crt'), // for verifying client certs
  requestCert: true,          // require client certificate
  rejectUnauthorized: true,   // abort if client cert is invalid
  // --- POODLE mitigations ---
  minVersion: 'TLSv1.2',      // disallow SSLv3, TLSv1.0, TLSv1.1
  maxVersion: 'TLSv1.3',
  ciphers: [
    'TLS_AES_128_GCM_SHA256',
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256'
  ].join(':'), // only AEAD suites
  honorCipherOrder: true
};

tls.createServer(options, (socket) => {
  console.log('mTLS connection authorized:', socket.getPeerCertificate().subject.CN);
  socket.write('HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK');
  socket.end();
}).listen(8443);

Java (using Jetty or plain SSLSocketFactory)

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;

SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("/etc/keystore/server.jks");
sslContextFactory.setKeyStorePassword("storepass");
sslContextFactory.setKeyManagerPassword("keypass");
sslContextFactory.setTrustStorePath("/etc/keystore/client-truststore.jks");
sslContextFactory.setTrustStorePassword("trustpass");
sslContextFactory.setNeedClientAuth(true); // mTLS

// POODLE mitigations
sslContextFactory.setIncludeProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
sslContextFactory.setExcludeCipherSuites(new String[]{
    ".*_CBC_.*",   // exclude all CBC suites
    ".*_RC4_.*",   // also drop RC4 if present
    ".*_DES_.*"
});
sslContextFactory.setIncludeCipherSuites(new String[]{
    "TLS_AES_128_GCM_SHA256",
    "TLS_AES_256_GCM_SHA384",
    "TLS_CHACHA20_POLY1305_SHA256"
});

ServerConnector connector = new ServerConnector(new Server(), sslContextFactory);
connector.setPort(8443);
Server server = new Server(connector);
server.start();

Go (using the standard crypto/tls package)

package main

import (
	"crypto/tls"
	"io"
	"log"
	"net"
)

func main() {
	cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
	if err != nil {
		log.Fatalf("load keys: %v", err)
	}
	caCertPool := tls.NewCertPool()
	caBytes, _ := io.ReadAll(fs.ReadFile("client-ca.crt"))
	if ok := caCertPool.AppendCertsFromPEM(caBytes); !ok {
		log.Fatalf("failed to parse client CA")
	}

	config := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tls.RequireAndVerifyClientCert,
		ClientCAs:    caCertPool,
		// --- POODLE mitigations ---
		MinVersion:   tls.VersionTLS12,
		MaxVersion:   tls.VersionTLS13,
		// Prefer AEAD suites, drop CBC
		CipherSuites: []uint16{
			TLS_AES_128_GCM_SHA256,
			TLS_AES_256_GCM_SHA384,
			TLS_CHACHA20_POLY1305_SHA256,
		},
	}

	ln, err := tls.Listen("tcp", ":8443", config)
	if err != nil {
		log.Fatalf("listen: %v", err)
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Printf("accept error: %v", err)
			continue
		}
		go func(c net.Conn) {
			io.WriteString(c, "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK")
			c.Close()
		}(conn)
	}
}

After applying these changes, rerun middlebrick scan. The Encryption check should now report a clean result (no SSLv3/TLS 1.0, no CBC ciphers), confirming that the POODLE attack surface has been eliminated while mutual TLS authentication remains intact.

Frequently Asked Questions

Does enabling mutual TLS automatically protect against the POODLE attack?
No. Mutual TLS only adds client‑certificate authentication; it does not change the record‑layer encryption. If the server still permits SSLv3/TLS 1.0 or CBC‑mode cipher suites, an attacker can downgrade the connection and exploit POODLE regardless of mTLS.
Can middleBrick detect POODLE‑prone configurations on an API that requires a client certificate?
Yes. middleBrick performs an unauthenticated black‑box scan of the TLS handshake. It reports any enabled SSLv3/TLS 1.0 versions or CBC cipher suites, even when the endpoint expects a client certificate, because the scan focuses on the encryption layer before application‑level authentication.