Java vulnerabilities of the week: 22 to 28 October 2026
Most security news reaches me as a CVE number with a score next to it, and from that I learn nothing I can use on Monday. What I can use is the mechanism: the value that was trusted, the layer that read the bytes differently, the default that switched a check off. That is what I go and look for in my own code, so once a week I take the Java side of the news apart. The longer argument is in the CVE stigma post.
This is the seven days to 28 October 2026. The window overlaps last Sunday’s post by four days, so I leave out what that one already covered (the HAPI FHIR, Abdera, Camel and Vysper items from 22 and 23 October), and the two items that start before the window are dated where they come up.
The theme picked itself: who the JVM thinks it is talking to. The JDK trust store lost four root certificates because eight eMudhra intermediate keys could be computed by anyone. netty-tcnative handed Java an SNI hostname with a NUL character inside that the wire never carried. Micronaut’s HTTP/3 server could attach another client’s certificate to a request, and a Kafka broker could accept a JWT without checking its signature. The two I take apart are the trust store, a process story, and the netty-tcnative encoding bug, a code one.
The week in five
-
Oracle Java SE and OpenJDK, two Security Alerts for the eMudhra incident, both CVSS 9.1.
CVE-2026-91403(21 October, one day before this window) shipped Java updates that distrust the compromised eMudhra intermediate certificates, andCVE-2026-114820(24 October) shipped updates that remove the related roots. In OpenJDK the first is change 8395142, which adds 16 certificates to the JDK’sblocked.certslist, and the second is 8395690, which deletesemsignrootcag1,emsignrootcag2,emsigneccrootcag3andcomodoaaacafromcacerts. Both are in every OpenJDK update repository from 8u to 27u, committed between 21 and 27 October. Oracle lists the October 2026 update builds as affected, from 8u511 to 27.0.1, so the October Critical Patch Update on its own includes neither change (first deep dive). -
Apache Kafka, 12 CVEs disclosed 28 October, fixed in 4.2.2, 4.3.2 and 4.4.0. The sharpest is
CVE-2026-110985(Important): withsasl.oauthbearer.jwks.endpoint.urlunset,DefaultJwtValidatorfalls back toClientJwtValidator, which does not verify the signature, so the broker accepts a forged token for any principal,"alg":"none"included. The fixed releases refuse to start in that configuration, and from 4.4.0 a broker with a JWKS endpoint also refuses to start until the expected issuer and audience are set (CVE-2026-110952, Moderate). Four Important issues share one shape, an allocation sized from a count the sender declared: protocol readers pre-sizing arrays before authorization (CVE-2026-111108), some 20 million tagged fields in a request header before authentication (CVE-2026-111115), a 90-byte produce request declaring a 2 GiB record body (CVE-2026-114349, which also needsmax.decompressed.message.bytesset, since its default is effectively unlimited), and the Streams and client deserializers (CVE-2026-111551). InCVE-2026-112375(Important) a principal with no grants can read broker files:IncrementalAlterConfigsis validated before it is authorized, validation resolves${file:...}through a config provider the request names, and the type-validation error returns the content. -
Jenkins core, 15 CVEs disclosed 28 October (13 High, 2 Medium), fixed in 2.588 and LTS 2.580.2. Jenkins deserializes configuration with XStream behind its own type filter (JEP-200), and XStream’s
resolves-toattribute names the class the converter actually instantiates. InCVE-2026-117612,RobustReflectionConverter.instantiateNewInstancetook that class from the attribute without consulting the filter, so a user with Overall/Read and either an account or permission to post aconfig.xmlcould instantiate blocked types, potentially reaching code execution. The fix checks theresolves-totype againstClassFilter.DEFAULTand swaps XStream’sAnyTypePermission.ANYfor a permission that consults the same filter. Also notable: an unauthenticated race that creates an administrator during the setup wizard (CVE-2026-117618), and a “remember me” cookie, obtainable with an API token, that outlives the token’s revocation (CVE-2026-117619). -
netty-tcnative, seven advisories disclosed 26 October (four with CVE IDs), fixed in 2.0.84.Final. netty-tcnative is the JNI layer between Netty and OpenSSL or BoringSSL, and all seven advisories, all reported by August829, concern that layer. The two rated High: a ring buffer that holds protocol bytes OpenSSL emits while the BIO is set for reading never reduced its write index modulo the buffer size, so after a wrap the next chunk length goes negative and is cast to
size_tin amemcpy(CVE-2026-116539, 8.1), and the BIO write callback reported more bytes written than it copied (CVE-2026-116541, 8.2). The rest are use-after-free and race bugs around session tickets, ALPN and callbacks, a private key task reporting success on failure, and the SNI bug in the second deep dive (CVE-2026-116542, Medium). The VulnCheck CVE records score these differently from the project’s advisories and give an older lower bound for the session ticket bug (CVE-2026-116540, 2.0.66.Final against 2.0.81.Final); I follow the project. The fix is on Maven Central since 7 September, and the Netty BOM manages it from 4.2.18.Final and in 4.1.139.Final. -
Micronaut, seven advisories disclosed 22 October, fixed in 4.10.33 and 5.2.17 (3.10.16 where 3.x is affected). The High one with the clearest mechanism: the HTTP/3 listener kept one field with the most recently created QUIC
SSLEngine, and each request stream resolved its TLS session from that field lazily, so under concurrent connectionsHttpRequest.getSslSession()could return another client’s session and peer certificate, which matters for mutual TLS and certificate-based authorization. HTTP/3 is experimental and off by default. The fix resolves session from the stream’s own QUIC connection. The HTTP client also accumulated Server-Sent Eventsdata:lines without limit until an event boundary a malicious server never sends (High), and the Netty server treated a request with a malformed chunk as a complete, empty body (Low). None has a CVE ID yet.
A few smaller ones. jackson-databind has a critical PolymorphicTypeValidator bypass, CVE-2026-110879 (9.8): the validator checks the outer class of a class-name type id and lets attacker-chosen classes through as generic type arguments or array element types. The advisory went up on GitHub on 20 October, two days before this window, and the CVE record on 28 October; fixed in 2.18.12, 2.21.8, 2.22.4, 3.1.8 and 3.2.4. Apache SIS 1.7 fixes two Low issues published on 26 October, path traversal through links between data files and an unquoted ORDER BY clause in the SQL feature store (CVE-2026-110748, CVE-2026-110733). And com.vaadin.external.google:android-json overflows the stack on deeply nested input and will not be fixed (CVE-2026-110974, 22 October), so the advice is to replace it.
The JDK trust store: a key is only as strong as the range it was drawn from
When a Java TLS client connects, the default trust manager builds a path from the server’s certificate to one of the roots in the JVM’s trust store, lib/security/cacerts unless something says otherwise, and if a path exists the server is trusted. Revocation is not part of that by default: PKIXValidator calls setRevocationEnabled(checkTLSRevocation) for TLS, and that flag is the system property com.sun.net.ssl.checkRevocation, false unless you set it.
On 17 October a community member, credited as Wayne, reported to the root programs that the private keys of eight eMudhra intermediate CAs had been recovered, six for TLS and two for code signing. Mozilla’s analysis in the incident bug explains the mechanism. These were P-384 keys, and a P-384 private key is supposed to be an integer chosen at random between 1 and the group order, a number of about 384 bits. All eight were integers below 256, every byte zero except the last. The public key is the private key times the curve’s generator point, so you compute G, 2G, 3G and so on, and among at most 255 candidates one matches the public key in the certificate. Mozilla recovered the keys independently within two hours of the report and noted the flaw was in the key generation, not in the signatures. eMudhra’s preliminary explanation is a script flaw in its April 2025 key ceremony that created a second, weaker key pair, used by mistake for the certificate requests; the full root cause report is pending. eMudhra revoked the intermediates on 18 October and Sectigo, which had cross-signed them under AAA Certificate Services, on 19 October.
Browsers were covered within hours by pushed blocklists, OneCRL and CRLSets. A JVM has no pushed list and does not check revocation by default, so the revocations did not reach a default Java client. Mozilla’s notice to trust store maintainers set out the options: block the intermediates if you have a mechanism for it, otherwise evaluate removing the roots, at the cost of some servers becoming unreachable. The JDK did both, in that order: change 8395142 put the 16 certificates, each intermediate as issued by eMudhra and as cross-signed by Sectigo, on blocked.certs, which UntrustedChecker consults during path validation whatever the revocation setting, and 8395690 removed the four roots three days later. Oracle published each step as a Security Alert with its own CVE, and thats how a trust store edit shows up in a scanner or an SBOM report.
Why I find it interesting
The class is weak key generation, insufficient entropy in the CWE-331 family, and what makes it a teaching case is how little there is to see. Every signature verified, the curve and the algorithm were the standard ones, the certificates were well formed. A P-384 key whose private scalar is below 256 has the same type, curve and encoding as a strong one, so no structural check catches it; you find it only by trying to recover the key, which is what the reporter’s scan of the CA database did. When you create keys in Java, let KeyPairGenerator draw them from a SecureRandom, and be wary of any path that builds a private key from a number it was handed, an ECPrivateKeySpec or an unwrap, because on that path the range is whatever the caller supplied.
The second lesson is where Java’s trust lives. It is a file, and it changes only when you change it: a JDK update, the OS package that generates the store for distribution JDKs (Debian’s /etc/ssl/certs/java/cacerts, Fedora and RHEL’s /etc/pki/java/cacerts), or an edit to a custom store passed with javax.net.ssl.trustStore. Container images freeze whichever one they were built with. The check takes a minute, keytool -list -cacerts | grep -iE 'emsign|aaa' on the JVM that actually runs your service, and an empty result is what you want. Expect PKIX path building failed from a few hosts afterwards: on oss-security Stuart Henderson pointed out that the InCommon intermediates also chain to emSign ECC Root CA - G3.
One detail from the blocklist commit I liked. blocked.certs matches by fingerprint, and an ECDSA signature (r, s) has a twin (r, n - s) that also verifies, so the build tool fingerprints the twin too. The commit makes it decide that from the signature algorithm, which belongs to the issuer, rather then from the certificate’s own key, which matters here because eight new entries are P-384 certificates signed by an RSA root.
netty-tcnative: a check on bytes does not survive a change of encoding
One of the values netty-tcnative passes up from OpenSSL or BoringSSL to Java is the SNI hostname, the server_name a client puts in its ClientHello so a server with several names can pick a certificate or a virtual host. On the native side SSL_get_servername() returns that name as a C string, and the one guarantee it carries, as the advisory puts it, is that the library rejects a raw 0x00 byte inside it. The other bytes are not validated, RFC 6066 says ASCII but the library does not enforce it.
netty-tcnative turned that C string into a Java String with JNI’s NewStringUTF() in two places, the servername callback that asks the application’s SniHostNameMatcher.match(long, String) to accept or reject the name, and the public SSL.getSniHostname(). NewStringUTF() does not decode UTF-8. It decodes modified UTF-8, the variant Java uses in JNI and in class files, where the character U+0000 is written as the two bytes 0xC0 0x80 precisely so that an encoded string never contains a zero byte. In standard UTF-8 those two bytes are an overlong encoding and invalid. So a ClientHello with 0xC0 0x80 in the hostname passes OpenSSL’s check, because there is no zero byte, and arrives in Java as a String with a real NUL inside. Call getBytes(StandardCharsets.UTF_8) on it later, for a log line, a file name or another native call, and the zero byte is back, the classic setup for truncation in anything that reads bytes as a C string. That is CVE-2026-116542, Medium, 6.5 in the project’s advisory, which notes that the real impact depends on what the application does with the name.
The fix, commit a879c85, replaces both calls with tcn_new_stringn(e, servername, strlen(servername)), which copies the bytes into a byte[] and calls new String(byte[]). A standard decoder produces U+0000 only from a zero byte, and OpenSSL already excluded those, so the guarantee now holds on both sides of the boundary.
Why I find it interesting
The family is encoding confusion across a boundary, CWE-172 and CWE-838 in the advisory’s terms, and its the same shape as a path normalization differential or HTTP request smuggling: two layers read the same bytes differently, and a check made by one of them does not hold for the other. Here the check in C was correct, and the property it established was lost one function call later, in the conversion to Java.
The Java specific part is that modified UTF-8 is all over the platform. JNI’s NewStringUTF and GetStringUTFChars use it, and so do DataOutputStream.writeUTF and DataInputStream.readUTF, which also decode 0xC0 0x80 to U+0000. Those are fine for strings your own code wrote. For bytes from a socket, a file or a peer, build the string from a byte[] with an explicit charset and validate after decoding, not before, because the validation has to look at the value your code will actually use. If you maintain JNI code, a grep for NewStringUTF with an argument that came from outside the process is a ten minute review.
What I take away from this week
First, where the fixes reach people, which differs item by item. The trust store changes ship with the JDK: a JDK update from your vendor (Oracle through its two alerts, other vendors from the OpenJDK update repositories), the OS CA package for distribution JDKs, or a manual edit for a custom trust store, and a container image rebuild in every case. Kafka is mostly a broker upgrade. The BOM bumps in Camel, Spring Boot and Quarkus move the client libraries, which covers the deserializer issue, but most of the rest is on the broker, and on 4.4.0 the broker will not start until issuer and audience are configured. For netty-tcnative the lever is the Netty BOM, so check which one your framework imports. Micronaut users bump the framework, and Jenkins is a controller upgrade. The fixed Kafka, netty-tcnative, Micronaut, Jackson and SIS versions are all on Maven Central and the Jenkins ones in the Jenkins repository; I checked.
Second, the theme from the top. In each of those four cases the identity decision rested on something that did not hold: a key that was not secret, a hostname that changed between C and Java, a TLS session from another connection, a token accepted without a signature check. When you review code that decides who is on the other end, ask where the value came from and what ties it to this connection, this key and this request.
Third, a quieter cluster: a size the sender declared, used as an allocation size. Kafka’s protocol readers, record decoder and Streams deserializers, and Micronaut’s SSE client, all trusted a count or a length that cost the sender a few bytes. The fixes look the same everywhere: cap the preallocation and grow on demand, check the declared size against the bytes actually present, and keep a cumulative budget for anything that accumulates.
One last observation, as last week. The Jenkins advisory credits the Anthropic OSS program on 13 of its 15 issues, three of them alongside researchers who found them independently, and Kafka’s CVE-2026-112375 credits Ada Logics with Anthropic Research and, separately, OpenAI Codex Security. I note it as the factual texture of the week, as in is there really a human in the loop; whoever finds the bug, the mechanism is still the part you act on.
References
- oss-security daily index for 28 October 2026 - Openwall oss-security
- Oracle Security Alert for CVE-2026-91403 - Oracle
- Oracle Security Alert for CVE-2026-114820 - Oracle
- CVE-2026-114820 record - CVE.org
- OpenJDK 8395142: CA Enhancements (blocked.certs) - openjdk/jdk on GitHub
- OpenJDK 8395690: CA certificate improvements (cacerts) - openjdk/jdk on GitHub
- eMudhra: Private key compromise of Intermediate Certificates - Mozilla Bugzilla
- oss-security: eMudhra Intermediates Compromise - Openwall oss-security
- oss-security: InCommon intermediates under emSign ECC Root CA - G3 - Openwall oss-security
- Protecting Firefox users following the compromise of eMudhra intermediate certificates - Mozilla Security Blog
- Apache Kafka CVE list - Apache Kafka
- oss-security: Apache Kafka OAUTHBEARER signature verification CVE-2026-110985 - Openwall oss-security
- oss-security: Apache Kafka issuer and audience validation CVE-2026-110952 - Openwall oss-security
- oss-security: Apache Kafka array allocation amplification CVE-2026-111108 - Openwall oss-security
- oss-security: Apache Kafka tagged-field allocation amplification CVE-2026-111115 - Openwall oss-security
- oss-security: Apache Kafka arbitrary broker file read CVE-2026-112375 - Openwall oss-security
- oss-security: Apache Kafka compressed record batch allocation CVE-2026-114349 - Openwall oss-security
- Jenkins Security Advisory 2026-10-28 - Jenkins
- oss-security: Multiple vulnerabilities in Jenkins - Openwall oss-security
- Jenkins SECURITY-4125 fix - jenkinsci/jenkins on GitHub
- netty-tcnative SNI hostname advisory GHSA-2784-hxmm-4hgf - netty/netty-tcnative on GitHub
- netty-tcnative ring buffer advisory GHSA-gf9q-9mg7-g5qh - netty/netty-tcnative on GitHub
- netty-tcnative fix: decoding of the SNI server_name - netty/netty-tcnative on GitHub
- Micronaut HTTP/3 TLS session advisory GHSA-9cqj-4442-98m4 - micronaut-projects/micronaut-core on GitHub
- Micronaut malformed chunk advisory GHSA-hc6g-27v9-95gw - micronaut-projects/micronaut-core on GitHub
- jackson-databind advisory GHSA-m8xf-2cr7-77ph - FasterXML/jackson-databind on GitHub
- Apache SIS: CVE-2026-110748 and CVE-2026-110733 - Apache mailing lists
- CVE-2026-110974 record - CVE.org
- Is there really a human in the loop? - oscerd.github.io
- The CVE stigma, we need a new security culture - oscerd.github.io