From: Daniel Owens via Fulldisclosure <fulldisclosure () seclists org>
Date: Thu, 29 Oct 2026 11:16:48 +0000
As of the current versions of the Apache Struts2 JSON Plugin (e.g., version 7.4.0 and the 7.5.0 snapshot dated 28
October 2026), attackers can bypass the element-count limit purported to protect Object deserialisation added to said
code as part of the WW-5618 and S2-072 "hardening controls". Please note that this vulnerability and the attack string
is different than the previously reported bypass against the JSON array deserialisation, which was discussed in detail
via "Continued Apache Struts2 JSON Plugin Insecure Deserialisation" (28 October 2026) and "Maximised JSON Array Attacks
-- "Let the Commas Hit the Floor" Proof of Concept" (28 October 2026). Said previous discussions pertained strictly to
bypassing protections on array deserialisation, with distinct code paths, mistakes, and attack strings.
Description:
The information system's JavaScript Object Notation (JSON) object parser enforces the `maxElements` resource limit by
checking the size of the `HashMap` being populated rather than by counting the number of key-value pairs actually being
parsed, so a payload consisting of entirely repeated keys never grows the map past a single entry while incurring both
memory and compute costs.
Impact:
An attacker who can send a request body to any endpoint protected by a `JSONInterceptor` configured with a
`maxElements` ceiling (including the default value) can construct a JSON object consisting of an arbitrarily large
number of duplicate-key pairs -- for example, the same key repeated hundreds of thousands of times -- bounded only by
the value of `struts.json.maxLength`, and have every pair be fully parsed, hashed, and boxed before the `maxElements`
check ever has a chance to trip since `Map.put(Object, Object)` with an already-present key never increases the value
`ret.size()`.
Attackers can force disproportionate CPU and allocation work, memory usage (similar to that discussed in "Continued
Apache Struts2 JSON Plugin Insecure Deserialisation" (28 October 2026) and "Maximised JSON Array Attacks -- "Let the
Commas Hit the Floor" Proof of Concept" (28 October 2026)) with a single request and exhaust server resources across
concurrent requests. For example, attackers can build a single object containing 262,143 String-Double key-value pairs
(only limited by `struts.json.maxLength`, to a 2MB string), with a single request consuming over 46MB and taking ~37ms
to parse and build on a pre-warmed system (assume 64-bit HotSpot JVM running with compressed oops and 8-byte alignment,
which is a standard configuration). Making several concurrent requests of such a nature can quickly create denial of
service conditions.
Examples:
Sample Vulnerable Code
`StrutsJSONReader.object()` in the `struts2-json-plugin` module, enforces the `maxElements` ceiling by comparing the
size of the `HashMap` being populated against the limit immediately before every `put(Object, Object)` call, as shown
below.
```java
protected Map<String, Object> object() throws JSONException {
if (this.depth >= this.maxDepth) {
throw new JSONException("JSON object nesting exceeds maximum allowed depth ("
+ this.maxDepth + "). Use " + JSONConstants.JSON_MAX_DEPTH + " to increase the limit.");
}
this.depth++;
try {
Map<String, Object> ret = new HashMap<>();
Object next = this.read();
if (next != OBJECT_END) {
String key = (String) next;
validateKeyLength(key);
while (this.token != OBJECT_END) {
this.read(); // should be a colon
if (this.token != OBJECT_END) {
if (ret.size() >= this.maxElements) {
throw new JSONException("JSON object exceeds maximum allowed elements ("
+ this.maxElements + "). Use " + JSONConstants.JSON_MAX_ELEMENTS + " to increase
the limit.");
}
ret.put(key, this.read());
if (this.read() == COMMA) {
Object name = this.read();
if (name instanceof String nextKey) {
key = nextKey;
validateKeyLength(key);
} else {
throw buildInvalidInputException();
}
}
}
}
}
return ret;
} finally {
this.depth--;
}
}
```
Code snippet from `struts-2-json-plugin`'s plugins/json/src/main/java/org/apache/struts2/json/StrutsJSONReader.java,
lines 156-196
Importantly, attackers can further couple this with the previously disclosed Apache Struts2 JSON Plugin `maxElements`
bypass in the `array()` method in the same class (please see the aforementioned Full Disclosure postings for details).
While attackers can choose from a variety of Objects, `Double` is the most damaging for this particular attack since
they incur the cost of building and boxing/unboxing and are never cached (unlike `Long` and `String` objects).
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/
Current thread:
- Apache Struts2 JSON Plugin Insecure Deserialisation -- Object Deserialisation Element-Count Limit Bypass Daniel Owens via Fulldisclosure (Nov 01)