Apache Parquet-Avro Schema Deserialization RCE/SSRF — Incomplete-Fix Bypass (CVE-2025-30065)
by micrictor · 2026-07-06
- Severity
- Critical
- CVE
- CVE-2025-30065
- Category
- misc
- Affected product
- Apache Parquet Java (parquet-avro module)
- Affected versions
- parquet-avro < 1.15.1 fully vulnerable (Vector A, java-class STRING fields); parquet-avro 1.15.1 and later remain vulnerable to a lower-severity bypass (Vector B, record name/namespace resolution) that the official fix does not cover
- Disclosed
- 2026-07-06
- Patch status
- patched
Tags
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-06 |
| Last Updated | 2026-07-06 |
| Author / Researcher | micrictor |
| CVE / Advisory | CVE-2025-30065 |
| Category | misc |
| Severity | Critical |
| CVSS Score | 9.8 (per NVD) |
| Status | PoC |
| Tags | rce, ssrf, unsafe-deserialization, parquet-avro, avro-schema, java-class, jvm, cwe-502, cwe-20, incomplete-fix, java |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Apache Parquet Java (parquet-avro module) |
| Versions Affected | parquet-avro < 1.15.1 fully vulnerable (Vector A, java-class STRING fields); parquet-avro 1.15.1 and later remain vulnerable to a lower-severity bypass (Vector B, record name/namespace resolution) that the official fix does not cover |
| Language / Platform | Java 17, Maven, JVM-based data pipelines (Spark/Flink/Hadoop-style consumers using AvroParquetReader) |
| Authentication Required | No |
| Network Access Required | Yes (attacker must get a crafted Parquet file into a victim ingestion path; SSRF variant requires attacker-reachable callback listener) |
Summary
CVE-2025-30065 is an unsafe class-instantiation vulnerability in Apache Parquet Java’s parquet-avro module: crafted Avro schema metadata embedded in a Parquet file can force the reading JVM to load and instantiate attacker-named classes. The official 1.15.1 patch introduced ReflectClassValidator to block the most dangerous vector (java-class on STRING fields, which allows single-argument String constructors such as javax.swing.JEditorPane(String url) to be called with attacker-controlled input, yielding confirmed SSRF). This PoC repository shows that fix is incomplete: a second vector — setting the Avro record’s name/namespace to the target class’s fully-qualified name — still causes SpecificData.getClass(schema) to call Class.forName() and invoke a no-arg constructor on every version including patched 1.15.1, because ReflectClassValidator is never consulted on that code path. The repository includes working demos for both vectors plus an honest write-up concluding the bypass is real but, absent a known no-arg-constructor gadget with dangerous side effects, its practical impact is more limited (DoS/file-write/environment-dependent JNDI) than the original CVSS 10.0 rating for the pre-fix vulnerability would suggest.
Vulnerability Details
Root Cause
Two independent unsafe reflection paths exist in parquet-avro’s Avro-schema-driven record materialization:
- Vector A —
java-classon STRING fields (fixed in 1.15.1):AvroRecordConverter.getStringableClass()reads ajava-classproperty from a STRING field’s schema and loads it viaClassUtils.forName().FieldStringableConverterthen looks upConstructor(String.class)and callsctor.newInstance(fieldValue)for every row, where both the class name and the string argument are attacker-controlled. 1.15.1 addedReflectClassValidator.PackageValidator, which throwsSecurityException: Forbidden class ...before the class loads. - Vector B — record name/namespace (NOT fixed in 1.15.1): When constructing an
AvroIndexedRecordConverter/AvroRecordConverter,getDatumClass()callsSpecificData.getClass(schema), which resolvesschema.getFullName()(namespace + name) viaClass.forName()directly —ReflectClassValidatoris never invoked on this path. This works identically on compat/non-compat mode and GenericData/SpecificData models, on all versions including 1.15.1.
Vector A payload schema (from src/main/java/com/example/WriteJEditorPanePayload.java):
| |
Vector B payload schema (from src/main/java/com/example/WriteSsrfPayload.java), which bypasses the 1.15.1 validator entirely by encoding the target class in the schema’s own name/namespace instead of a java-class property:
| |
Attack Vector
- Attacker crafts a Parquet file whose embedded Avro schema either (a) sets a STRING field’s
java-classproperty to a dangerous class with a usefulString-argument constructor (Vector A, pre-1.15.1 only), or (b) sets the schema’sname/namespaceto the fully-qualified name of a class already present on the victim’s classpath (Vector B, all versions). - Attacker delivers the file into any workflow that parses it with
AvroParquetReader(batch upload, object-store ingestion, ETL/pipeline import, ad-hoc analytics query). - On read,
parquet-avroresolves the schema and callsClass.forName()on the attacker-supplied class name, then invokes either aStringconstructor (Vector A, attacker-controlled argument) or a no-arg constructor (Vector B). - Depending on the target class, this triggers a static initializer and/or constructor side effect: SSRF (
JEditorPane(String url)), DoS (org.apache.derby.jdbc.EmbeddedDriverbooting an embedded DB engine), arbitrary file creation (java.util.logging.FileHandler), or environment-dependent JNDI lookups (javax.naming.InitialContext).
Impact
Vector A (pre-1.15.1) gives confirmed SSRF using only JDK classes, and potential RCE if a classpath gadget exposes a dangerous single-String-argument constructor. Vector B survives the official patch on all versions including 1.15.1 and, while limited to a no-arg-constructor primitive with no confirmed public RCE gadget, still enables arbitrary class loading/static-initializer execution, DoS, and unauthorized file writes on any JVM that parses attacker-influenced Parquet data — a materially different (lower but non-trivial) risk than the pre-fix CVSS 10.0 rating implies.
Environment / Lab Setup
OS: Linux/macOS
JDK: Java 17
Build: Maven (mvn -q clean package / shaded jar via maven-shade-plugin)
Dependencies: org.apache.parquet:parquet-avro:1.15.1 (pom.xml pins the patched version to demonstrate the bypass)
org.apache.parquet:parquet-hadoop:1.15.1
org.apache.avro:avro:1.12.0
org.apache.hadoop:hadoop-common:3.3.6 / hadoop-mapreduce-client-core:3.3.6
org.slf4j:slf4j-simple:2.0.9
Vulnerable dep for Vector A demo: swap parquet.version to a pre-1.15.1 release (e.g. 1.15.0) in pom.xml
Proof of Concept
PoC Script
See the Java sources in
src/main/java/com/example/in this folder, and the detailed writeup inparquet_avro_rce_report.md:
WritePoC.java/WritePoC2.java— write malicious Parquet payloads for Vector B (record name) and thejava-classSTRING field vectorWriteSsrfPayload.java/SsrfDemo.java— Vector B end-to-end SSRF demo (bypasses 1.15.1 fix) viaSsrfCanary’s static initializerWriteJEditorPanePayload.java/JEditorPaneSsrfDemo.java— Vector A SSRF demo viajavax.swing.JEditorPane(blocked on >= 1.15.1)CanaryClass.java/SsrfCanary.java— canary gadget classes proving arbitrary class loading/instantiationReadPoC.java— reads a malicious Parquet file under four reader configurations to show which trigger the vulnerable code path
| |
Detection & Indicators of Compromise
Signs of compromise:
- Static-initializer or constructor side effects (network calls, file writes) observed during Parquet/Avro schema resolution, not during actual row-data processing
- Outbound requests from data-pipeline nodes to unfamiliar hosts shortly after ingesting a new Parquet file
SecurityException: Forbidden classentries in parquet-avro reader logs- Unexplained
derby.logfiles or newjava.util.logging.FileHandleroutput on hosts that parse untrusted Parquet data
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade to a parquet-avro release addressing CVE-2025-30065 (1.15.1) for Vector A; note that Vector B (record name/namespace resolution via SpecificData.getClass()) is not covered by the 1.15.1 ReflectClassValidator fix as of this repo’s research — track upstream Apache Parquet advisories for a follow-up patch |
| Interim mitigation | Never parse Parquet/Avro files from untrusted sources with default AvroParquetReader settings; validate/sanitize schema metadata (record name, namespace, and any java-class properties) before read; restrict SERIALIZABLE_PACKAGES/classpath exposure of dangerous gadget classes; run ingestion JVMs with restricted egress and minimal classpath to limit exploitable no-arg-constructor gadgets |
References
Notes
Mirrored from https://github.com/micrictor/parquet-avro-rce on 2026-07-06. This repo does not merely reproduce CVE-2025-30065 — it demonstrates that the official 1.15.1 fix (ReflectClassValidator) only blocks one of two exploitable class-instantiation vectors (java-class on STRING fields). The second vector, driven by the Avro schema’s record name/namespace, bypasses the validator entirely on all tested versions including 1.15.1, though the author’s own analysis (parquet_avro_rce_report.md) honestly notes no confirmed RCE/SSRF gadget has been found for that no-arg-constructor-only primitive on standard classpaths, so real-world severity of the bypass is lower than the original CVE’s CVSS 10.0 rating.