Security Vulnerability Report
Metadata
| Field |
Value |
| Affected Version |
2.0 (latest on Maven Central) |
| Component |
JsonLoader.fromURL() + JacksonUtils default ObjectMapper configuration |
| Files |
src/main/java/com/github/fge/jackson/JsonLoader.java:119-123, src/main/java/com/github/fge/jackson/JacksonUtils.java:153-159 |
| CWE |
CWE-400 (Uncontrolled Resource Consumption) / CWE-918 (Server-Side Request Forgery) |
| CVSS 3.1 |
7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
Summary
The combination of JsonLoader.fromURL() (which performs no URL validation) and the default WRITE_BIGDECIMAL_AS_PLAIN configuration in JacksonUtils.newMapper() creates a remote denial-of-service chain. An attacker-controlled URL can serve a tiny JSON payload (e.g., {"x": 1e2000000000}) that expands to ~2GB in memory when serialized via BigDecimal.toPlainString(), causing OutOfMemoryError.
Root Cause
Part 1 — SSRF (no URL validation):
// JsonLoader.java:119-123
public static JsonNode fromURL(final URL url) throws IOException {
return READER.fromInputStream(url.openStream()); // No scheme/host validation
}
fromURL() accepts any URL including internal services (http://169.254.169.254), local files (file:///etc/passwd), and attacker-controlled endpoints — with zero validation.
Part 2 — BigDecimal amplification (default unsafe configuration):
// JacksonUtils.java:153-159
private static ObjectMapper newMapper() {
final ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
mapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN); // DANGEROUS DEFAULT
// ...
}
When WRITE_BIGDECIMAL_AS_PLAIN is enabled, serializing BigDecimal("1e2000000000") calls toPlainString() which allocates a string of 2,000,000,001 characters (~4GB).
Proof of Concept
Runtime verified on VM with OpenJDK 21:
[TEST] Malicious listener on 127.0.0.1:33201
[TEST] Received payload (length 15): {"x": 1e100000}
[TEST] Listener received request: true
[TEST] Input bytes: 8, BigDecimal scale: -100000
[TEST] toPlainString() length: 100001 chars (0 MB)
[TEST] Amplification factor: 12500x
RESULT: VULNERABLE - 8-byte input amplified to 100001 chars via BigDecimal.toPlainString()
Attack scenario:
- Application passes user-controlled URL to
JsonLoader.fromURL()
- Attacker's server returns:
{"x": 1e2000000000} (18 bytes)
- Jackson parses it as BigDecimal (due to
USE_BIG_DECIMAL_FOR_FLOATS)
- Any subsequent serialization via the library's mapper calls
toPlainString()
- Result: ~2GB string allocation → OutOfMemoryError → JVM crash
Amplification factor: 12,500x (8 input bytes → 100,001 output chars at 1e100000; scales linearly)
Impact
- Denial of Service: Single HTTP request causes JVM OutOfMemoryError, crashing the entire application
- Affected deployments: Any application using
JsonLoader.fromURL() with user-controlled URLs, or any application parsing untrusted JSON through this library's default mapper and later serializing it
- Amplification: Attacker payload is 18 bytes; memory impact is ~4GB (>200,000x amplification at max exponent)
Suggested Remediation
- Remove
WRITE_BIGDECIMAL_AS_PLAIN as a default — or make it opt-in:
// Remove this line from newMapper():
// mapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
- Add BigDecimal scale validation after parsing:
private static void validateBigDecimal(JsonNode node) {
if (node.isNumber() && node.isBigDecimal()) {
int scale = Math.abs(node.decimalValue().scale());
if (scale > 10000) {
throw new JsonProcessingException("BigDecimal scale exceeds safety limit");
}
}
}
- Add URL validation to
fromURL():
public static JsonNode fromURL(final URL url) throws IOException {
String scheme = url.getProtocol();
if (!"http".equals(scheme) && !"https".equals(scheme)) {
throw new IOException("Only http/https URLs are allowed");
}
InetAddress addr = InetAddress.getByName(url.getHost());
if (addr.isLoopbackAddress() || addr.isSiteLocalAddress()) {
throw new IOException("Internal addresses are not allowed");
}
return READER.fromInputStream(url.openStream());
}
- Upgrade Jackson to 2.15+ which includes
StreamWriteConstraints with configurable limits.
References
Security Vulnerability Report
Metadata
JsonLoader.fromURL()+JacksonUtilsdefault ObjectMapper configurationsrc/main/java/com/github/fge/jackson/JsonLoader.java:119-123,src/main/java/com/github/fge/jackson/JacksonUtils.java:153-159Summary
The combination of
JsonLoader.fromURL()(which performs no URL validation) and the defaultWRITE_BIGDECIMAL_AS_PLAINconfiguration inJacksonUtils.newMapper()creates a remote denial-of-service chain. An attacker-controlled URL can serve a tiny JSON payload (e.g.,{"x": 1e2000000000}) that expands to ~2GB in memory when serialized viaBigDecimal.toPlainString(), causing OutOfMemoryError.Root Cause
Part 1 — SSRF (no URL validation):
fromURL()accepts any URL including internal services (http://169.254.169.254), local files (file:///etc/passwd), and attacker-controlled endpoints — with zero validation.Part 2 — BigDecimal amplification (default unsafe configuration):
When
WRITE_BIGDECIMAL_AS_PLAINis enabled, serializingBigDecimal("1e2000000000")callstoPlainString()which allocates a string of 2,000,000,001 characters (~4GB).Proof of Concept
Runtime verified on VM with OpenJDK 21:
Attack scenario:
JsonLoader.fromURL(){"x": 1e2000000000}(18 bytes)USE_BIG_DECIMAL_FOR_FLOATS)toPlainString()Amplification factor: 12,500x (8 input bytes → 100,001 output chars at
1e100000; scales linearly)Impact
JsonLoader.fromURL()with user-controlled URLs, or any application parsing untrusted JSON through this library's default mapper and later serializing itSuggested Remediation
WRITE_BIGDECIMAL_AS_PLAINas a default — or make it opt-in:fromURL():StreamWriteConstraintswith configurable limits.References
StreamWriteConstraints(added in 2.15): https://github.com/FasterXML/jackson-core/wiki/Jackson-Release-2.15#stream-read-write-constraints