Java vulnerabilities of the week: 19 to 25 October 2026
A CVE number tells you something exists and a severity tells you roughly how loud to be about it, and neither one tells you what to go and look for in your own code. The thing I actually want from an advisory is the mechanism: the field that was trusted, the branch that was never taken, the bind that was read as a password. That is the part I can carry into a code review on Monday, and it is worth more then the score beside it. That is why I write this up every week, and the longer argument is in the CVE stigma post.
This is the seven days to 25 October 2026, a heavily Apache week, almost all of it server and library code that Java shops actually run. Two shapes turn up again and again. The first is an authorization or authentication decision that came out permissive, or was missing entirely: a fail-open authentication in Tomcat, missing authorization across tenants in Pulsar, a critical function left unauthenticated in IoTDB. The second is a component that reads input it did not produce with the parser’s trust left switched on: a deserializer in Camel, an XML parser in three projects. The two I pull apart below are one of each.
A note before the digest: I reported or wrote the fix for several of this week’s Camel items, so I will keep to the facts of the mechanism and the versions, and credit the external finders where there is one.
The week in five
-
Apache Camel, eight CVEs across two disclosures, fixed in 4.23.0 with backports to the 4.22.x and 4.18.x lines. The first,
CVE-2026-88900(High, CVSS 8.8, disclosed 19 October), is an unsafe Java deserialization incamel-jgroups: the consumer deserializes the payload of an inboundObjectMessagethroughObjectInputStreambefore any Camel code runs, on a default channel that uses UDP multicast with noAUTHand no encryption, which is remote code execution for anyone who can reach the group given a gadget on the classpath (first deep dive). Fixed in 4.18.5, 4.22.1 and 4.23.0, found by h1ei1. The second disclosure, on 23 October, is a batch of seven fixed in 4.18.5, 4.22.2 and 4.23.0.CVE-2026-90885(High, 7.5) is an XXE incamel-saxon, whereXQueryBuilderleft Saxon’s parser acceptingDOCTYPEand external entities for a body already typed as aSource, found by Leon Zlobecki.CVE-2026-94386(Medium) is cross-scope token reuse incamel-oauth, a static client-credentials cache keyed only on the token endpoint and client id, leaving out the secret and scope.CVE-2026-95663(Medium) iscamel-keycloakaccepting expired tokens because the introspection cache used a fixed time to live that ignored the token’s ownexp. The rest are path handling incamel-docling(CVE-2026-95628andCVE-2026-95629), an evaluated-instead-of-literalCamelAwsS3Keyheader incamel-aws2-s3(CVE-2026-95664), and unmuted consumer exceptions incamel-coap(CVE-2026-101259). -
Apache Tomcat, seven CVEs disclosed 20 October, fixed in 11.0.27, 10.1.61 and 9.0.123. Three are rated Important.
CVE-2026-101296is aJNDIRealmauthentication bypass: with a non-GSSAPI SASL bind mechanism configured, the credential-check bind ran under that mechanism instead of a simple bind and could complete without checking the password, so a wrong password authenticated (second deep dive), reported by Sreewathsa Kovalluri, Shon Babu and Kannan K of Innspark Solutions.CVE-2026-101356is an HTTP/2 request and response mix-up after an error during async processing, from incorrect object recycling, where one user’s request or response can surface in another’s.CVE-2026-103119is a buffer under-read on large non-blocking writes that can split a response body between the current request and the next on the same connection (11.x only), found by Tuomo Hänninen. The four Low are HTTP/2 trailer data leaking into the next request (CVE-2026-88041), intermittent TLS 1.3 connection failures (CVE-2026-93446), the Jasper JSTL<c:redirect>not aborting the page (CVE-2026-74049), and an OpenSSL-FFM FIPS activation failure that keeps running in non-FIPS mode (CVE-2026-74048). -
Apache Pulsar, 14 CVEs disclosed 21 October in one combined announcement from the PMC, fixed in 4.0.14, 4.2.5 and 5.0.0. The thread through nearly all of them is cross-tenant authorization.
CVE-2026-105869(Critical, 9.2) is the worst: withMultiRolesTokenAuthorizationProviderand an anonymous role configured, HTTP and WebSocket requests that failed authentication still took their roles from the unverified token, so a forged token could be handed superuser. The four High issues all skip a check across a tenant boundary: a shadow-topic source attached to another tenant’s topic (CVE-2026-105937), a proxied request authorized with the proxy’s own credentials (CVE-2026-105942), unvalidated package version components reaching foreign tenants’ packages (CVE-2026-105945), and partitioned-topic truncation running as the broker’s internal admin (CVE-2026-105934). The medium and low issues are more of the same on schema, functions, transaction-admin and entry-filter endpoints. Separately, Pulsar Manager getsCVE-2026-105944(missing authentication and admin credential disclosure) and is now end of life. -
Apache IoTDB, four CVEs disclosed 21 October, fixed in 2.0.11 (affected 2.0.0 up to 2.0.11).
CVE-2026-59562(Important) is the one to move on: the legacy pipesendFileRPC had missing authentication for a critical function, which is unauthenticated remote code execution and path traversal, found by tonghuaroot and Opr4. Alongside it are an authentication bypass in thepipeTransfer()interface (CVE-2026-58663), privilege escalation through the legacy pipe (CVE-2026-59229), and a JDBC driver memory exhaustion whereBigDecimal.toPlainStringexpands a crafted value into a very large string (CVE-2026-41114). -
HAPI FHIR, four CVEs disclosed 22 October through a CISA coordinated advisory (VU#394579). HAPI FHIR is the widely deployed Java implementation of the HL7 FHIR standard, not an Apache project, but Java and common in healthcare integrations.
CVE-2026-113874(High, 8.1, CWE-863) is the sharp one: a caller adds a partition-selection extension to a bundle entry and HAPI overwrites the gateway-set tenant header with it, with no entitlement check, reaching another tenant’s partition.CVE-2026-113873(High, 7.5) is a gzip request body decompressed before authorization with no size or ratio cap, so a small request can exhaust the heap.CVE-2026-113876(Medium) is a conditional create matching exactly one Patient and returning the full record to a caller who has create but not read, andCVE-2026-113875(Low) is REST-hook subscription delivery running with system access to a caller-controlled callback. The honest caveat: as of disclosure there is no released fix for the first three, and the conditional-create path is slated for 9.0.0 (planned 19 November 2026). For HAPI FHIR, unlike the rest of this list, there is no fixed release to move to yet, so the action is mitigation now. Tested on 8.10.0 with synthetic data, reported by Abhinav Agarwal.
Honourable mention for three long-dormant Apache projects that all surfaced this week. Apache Cocoon took four (CVE-2026-108086 XML external entity leading to SSRF, CVE-2026-108087 SQL injection, CVE-2026-108088 cross-user cache poisoning, CVE-2026-108089 unauthenticated access to diagnostic interfaces, all 20 October, all versions affected), and the maintainers say they will not release a fix and recommend restricting access or moving off it. Apache Abdera got CVE-2026-113582, an XXE in Atom document parsing (22 October), and Apache Vysper got CVE-2026-115721 (unbounded allocation) and CVE-2026-115748 (authorization bypass, 23 October). If any of these are in your stack, the practical move is a migration, because for Cocoon there is no fix coming.
Apache Camel: a message off the cluster is untrusted input
Camel can run as a cluster of nodes, and camel-jgroups is one way they talk, using JGroups to pass messages over a shared channel. The producer sends Java objects wrapped as a JGroups ObjectMessage, and the consumer receives whatever arrives on the channel and turns it into a Camel exchange your routes then process.
The receive path is short. CamelJGroupsReceiver hands each inbound message to JGroupsEndpoint.createExchange(Message), and that method sets the exchange body from org.jgroups.Message.getObject(). For an ObjectMessage, which is what the Camel producer emits, JGroups deserializes the payload through a plain ObjectInputStream, on its own receive thread, as the bytes arrive, before any Camel code runs. Nothing filters that stream unless the deployment has set a JVM-wide jdk.serialFilter, and neither camel-jgroups nor the affected JGroups builds installed an ObjectInputFilter of their own.
The other half is the channel. With no channel or channelProperties set, the consumer builds a stock JChannel, which multicasts over UDP with no AUTH protocol and no encryption. So any host that can reach the multicast group or the bind port can join the cluster and inject a message, with no credentials, and a crafted serialized object will execute code as it is read, given a usable gadget chain on the application’s classpath. That is CVE-2026-88900, CWE-502, rated High at CVSS 8.8. It is High rather than Critical because the attacker needs network reach to the group and a usable gadget already on the classpath.
The fix came in two parts. A new deserializationFilter option takes a pattern in jdk.serialFilter syntax, falling back when unset to a JVM-wide jdk.serialFilter or a built-in allow-list, and because it runs after JGroups has already built the object the advisory is explicit that it is defense in depth. The second part, in 4.23.0, is a startup guard: the consumer refuses to start unless a control that acts before deserialization is in place, meaning a JVM-wide jdk.serialFilter, the jgroups.deserialization.filter honored by JGroups 5.5.7.Final (which 4.23.0 ships), or a channel with AUTH plus encryption. Setting acceptAllObjects=true restores the old behavior and is refused under the prod profile. On 4.22.1 and 4.18.5 there is no startup guard and the bundled JGroups does not honor the filter property, so there you add a jdk.serialFilter or an authenticated and encrypted channel yourself.
Why I find it interesting
The class is deserialization of untrusted data, CWE-502. The gadget chain is the exploit, but the vulnerability is calling ObjectInputStream on bytes you did not produce, and its worth separating those two things when you review your own code.
The detail that makes it a teaching case is timing. The dangerous work happens while the bytes are being read, inside the readObject of whatever classes the stream names, before you ever hold the finished object. A check that inspects the resulting object is already too late, because by then any embedded code has run. A serialization filter is only useful if it runs during the read, deciding per class before each one is instantiated, which is exactly why the Camel option is defense in depth and why the guard that holds is a filter at the JVM or JGroups layer, or not taking the untrusted bytes at all.
The second detail is the trust boundary, and it is the one I would fix first. The default channel trusts the network: no AUTH, no encryption, UDP multicast. The deserialization is only reachable because the transport let an unauthenticated stranger put bytes on the wire, so authenticate and encrypt the channel and those bytes stop being untrusted, which is why AUTH with encryption satisfies the startup guard on its own. This is the same bug class as CVE-2026-40860 in camel-jms. When a component’s job is to carry objects between processes, “receive an object” and “deserialize whatever arrives” are the same line of code unless someone draws the boundary on purpose. Any Java code that reads an ObjectMessage or an ObjectInputStream off a transport that is not itself authenticated has this shape.
Apache Tomcat: a bind that completed is not a password that matched
Tomcat’s JNDIRealm authenticates users against an LDAP or directory server. In bind mode it checks a password the way the directory would: it binds to the server as that user with the supplied password, and a simple bind verifies the password, so a bind that succeeds means the password was correct and a wrong one is rejected with an error. The realm leans on that, a successful bind standing in for a matched password. I did not work on this one; it was reported by Sreewathsa Kovalluri, Shon Babu and Kannan K of Innspark Solutions on 14 September and made public on 20 October.
The bug is in bindAsUser. The code set up the bind using whatever Context.SECURITY_AUTHENTICATION the realm was configured with, and it forced the simple mechanism in only one case, by removing the setting when it was GSSAPI. If the realm was configured with some other, non-GSSAPI SASL mechanism, that mechanism stayed in effect and the credential-check bind ran under it. The surrounding catch treats a thrown NamingException as a failed attempt and logs it. A SASL bind under a non-simple mechanism can complete without checking the supplied password the way a simple bind does, so the bind returns without throwing, and a bind that returned without throwing was counted as a success. The outcome is authentication without the correct password, CWE-287 reached by failing open, rated Important and fixed in 11.0.27, 10.1.61 and 9.0.123. The fix, commit e43453f, titled “Always use a simple bind to validate a user and password via a bind,” sets Context.SECURITY_AUTHENTICATION to simple for the validation bind every time and restores the configured mechanism afterward.
Why I find it interesting
Two things, and the first is the success condition. It was implicit: the credential counted as valid when the bind returned without an error. The stronger form is explicit and positive, a result that says this credential was verified by this check. “The operation did not throw” is a weaker claim, satisfied by any path where the operation does something other than what you assumed. Here the assumption was that a bind checks the password, which holds for a simple bind and is not guaranteed for an arbitrary SASL mechanism. The bug lives in the gap between “the bind completed” and “the password was checked.”
The second is the general rule, and it is the part I would carry into a review. The class is improper authentication, CWE-287, in the fail-open variety: a security decision made by performing an operation and watching for an exception. If the absence of an error is your proof, you are trusting that the operation fails loudly on exactly the condition you care about, and that is a property of the operation you have to verify, not assume. The fix ties password validation to a simple bind, whose success does mean the password matched. The same principle covers the separate GSSAPI-bind case in CVE-2026-55957: each authentication path needs its own reason that success means the credential was verified.
What I take away from this week
First, where these fixes reach people. Camel users mostly move by the bill of materials: bump the Camel BOM to 4.23.0 (or the backport line, 4.22.2 or 4.18.5) and Camel Quarkus and Camel Spring Boot follow, though anyone on camel-jgroups at 4.22.1 or 4.18.5 should also add a control that runs before deserialization, since those lines have no startup guard. Tomcat usually arrives through Spring Boot’s managed version, so a Spring Boot patch bump pulls 11.0.27, 10.1.61 or 9.0.123, while standalone installs patch directly. Pulsar and IoTDB are servers you upgrade in place, to 4.0.14, 4.2.5 or 5.0.0 and to 2.0.11. HAPI FHIR is the exception: three of its four have no released fix, so there is nothing to bump yet, and the work is mitigation now and an upgrade when 9.0.0 ships. I checked the Camel, Tomcat, Pulsar and IoTDB versions on Maven Central and they are all published; the HAPI FHIR fixes are not.
Second, the shape that ties most of the week together: the decision meant to deny access came out permissive, or was missing. Tomcat counted a completed bind as a verified password. Pulsar authorized across tenants on endpoints that did not run the tenant check, and on a failed authentication took roles from a token it had not verified. IoTDB left a critical pipe function without authentication. HAPI FHIR let a request choose its own tenant partition. Camel-Keycloak served a token from cache past its own expiry. The rejection is the branch that is easy to leave out, because the success branch is the one you exercise by hand while you build the thing. When you read authentication or authorization code, the question to force is not “does the happy path work,” its “what exactly makes this return failure, and have I made it take that branch.”
Third, the quieter cluster: components that read input they did not produce with the parser’s trust left on. camel-jgroups deserialized bytes off the network, and camel-saxon, Cocoon and Abdera parsed XML with DOCTYPE and external entities enabled. These defaults are old and they trust the document, so you have to turn the trust off deliberately, with a serialization filter that runs before the object is built and a parser configured to reject DOCTYPE and external entities. If a component’s job is to read bytes from somewhere else, the safe stance is the bytes are hostile until a boundary you control says otherwise.
One last observation, since I have written about it before. Every finder credited this week is a person, from h1ei1 and Leon Zlobecki on Camel to the Innspark team on Tomcat and Abhinav Agarwal on HAPI FHIR. That is a change from the run of AI-assisted finds I looked at in is there really a human in the loop, and I note it only as the factual texture of the week. Whoever or whatever finds the bug, the mechanism is still the part you act on.
References
- oss-security daily index for 19 October 2026 - Openwall oss-security
- oss-security: Apache Camel camel-jgroups CVE-2026-88900 - Openwall oss-security
- Apache Camel security advisory CVE-2026-88900 - Apache Camel
- CVE-2026-88900 record - CVE.org
- oss-security daily index for 23 October 2026 - Openwall oss-security
- oss-security: Apache Camel camel-saxon CVE-2026-90885 - Openwall oss-security
- oss-security: Apache Camel camel-oauth CVE-2026-94386 - Openwall oss-security
- oss-security: Apache Camel camel-keycloak CVE-2026-95663 - Openwall oss-security
- oss-security daily index for 20 October 2026 - Openwall oss-security
- Apache Tomcat 11 security page - Apache Tomcat
- oss-security: Apache Tomcat JNDIRealm CVE-2026-101296 - Openwall oss-security
- CVE-2026-101296 record - CVE.org
- Tomcat JNDIRealm fix: always use a simple bind - apache/tomcat on GitHub
- oss-security: Apache Tomcat AJP response mix-up CVE-2026-103119 - Openwall oss-security
- oss-security daily index for 21 October 2026 - Openwall oss-security
- oss-security: Apache Pulsar, 14 CVEs - Openwall oss-security
- oss-security: Apache IoTDB legacy pipe sendFile CVE-2026-59562 - Openwall oss-security
- CVE-2026-59562 record - CVE.org
- oss-security daily index for 22 October 2026 - Openwall oss-security
- oss-security: HAPI FHIR CVE-2026-113873 to CVE-2026-113876 - Openwall oss-security
- oss-security: Apache Cocoon CVE-2026-108086 - Openwall oss-security
- Is there really a human in the loop? - oscerd.github.io
- The CVE stigma, we need a new security culture - oscerd.github.io