CVE-2026-59171
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
JSON-java (org.json) is a reference implementation of a JSON package in Java. It provides the JSONObject, JSONArray, and JSONTokener classes used to parse, build, and serialize JSON text, plus converters between JSON and XML, CSV, cookies, and HTTP headers. It is widely embedded as a lightweight JSON layer in Java applications, and the project ships date-stamped releases (e.g. 20230227) rather than semantic versions.
A denial of service vulnerability (CVE-2026-59171) has been identified in the numeric value conversion of JSON-java's JSONObject and XML classes, which allows attackers to exhaust CPU time by sending JSON or XML input that contains very long numeric literals. Short exponent literals such as 1E100000000 can also stall a thread when the application reads them with getBigInteger or optBigInteger.
Per OWASP: The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed.
This issue affects JSON-java versions 20260522 and earlier.
Details
Module Info
- Product: JSON-java
- Affected packages:
org.json:json - Affected versions: <=20260522
- GitHub repository: https://github.com/stleary/JSON-java
- Published packages: https://central.sonatype.com/artifact/org.json/json
- Package manager: Maven
- Fixed in:
- OSS JSON-java (org.json:json) 20260719
- NES for JSON-java v20230227.0.3
Vulnerability Info
This Medium-severity vulnerability is found in the org.json:json package in JSON-java versions 20260522 and earlier.
When JSON-java parses JSON text with new JSONObject(String), new JSONArray(String) or JSONTokener, every unquoted value is passed to JSONObject.stringToValue. XML.toJSONObject passes element text and attribute values to the equivalent XML.stringToValue. Both methods hand any value that starts with a digit or - to stringToNumber, with no limit on its length:
char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
try {
return stringToNumber(string);
} catch (Exception ignore) {
}
}
return string;stringToNumber then builds the number with new BigDecimal(val) for decimal or exponent notation, and with new BigInteger(val) otherwise. Converting a decimal string to a BigInteger takes time that grows faster than linearly with the number of digits, so a single literal of a few hundred thousand digits, well within common request body limits, keeps the parsing thread busy for seconds. Repeating such requests ties up a server's worker threads and denies service to legitimate users.
A short exponent literal is stored compactly as a BigDecimal, so it passes through parsing quickly. It expands only when JSONObject.objectToBigInteger, the helper behind getBigInteger and optBigInteger on both JSONObject and JSONArray, converts it without checking the size of the result:
if (val instanceof BigDecimal){
return ((BigDecimal) val).toBigInteger();
}
// ...
final String valStr = val.toString();
if(isDecimalNotation(valStr)) {
return new BigDecimal(valStr).toBigInteger();
}An 11-character value such as 1E100000000 makes toBigInteger() materialize an integer of about 100 million digits, which stalls the thread for a long time or exhausts the heap.
Steps to Reproduce
- Add
org.json:jsonversion 20230227 (or any version up to 20260522) to the classpath. - Compile and run the following class:
import org.json.JSONObject;
import org.json.XML;
public class Poc {
public static void main(String[] args) {
String digits = "9".repeat(500_000);
long start = System.currentTimeMillis();
new JSONObject("{\"x\":" + digits + "}");
System.out.println("JSONObject: " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
XML.toJSONObject("<r>" + digits + "</r>");
System.out.println("XML: " + (System.currentTimeMillis() - start) + " ms");
JSONObject exp = new JSONObject("{\"x\":1e100000000}");
start = System.currentTimeMillis();
exp.optBigInteger("x", null);
System.out.println("optBigInteger: " + (System.currentTimeMillis() - start) + " ms");
}
}- Observe that each 500,000-digit value takes several seconds to parse and that
optBigIntegeron the exponent literal blocks for much longer. A fixed version keeps the long literal as a string and returns the default value fromoptBigIntegeralmost immediately.
Mitigation
Only the most recent JSON-java release is community-supported. Older releases are End-of-Life and have no publicly available fix; NES for JSON-java is the remedy for them.
Users of the affected components should apply one of the following mitigations:
- Upgrade org.json:json to release 20260719 or later, which contains the fix.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- waydeshi (finder)