{"schema":"threatline.record/2","record":{"id":"GHSA-h2j4-28h8-cj5v","aliases":["GHSA-h2j4-28h8-cj5v"],"summary":"Gremlin Groovy engine missing scripting authorization check enables authenticated RCE","details":"## Summary\n\nWhen the ArcadeDB Gremlin module is configured to use the Groovy script engine (`arcadedb.gremlin.engine` set to `groovy` or `auto`), any authenticated user, including those with only the read-only `reader` role, can execute arbitrary Groovy code on the server by submitting a Gremlin command to the `POST /api/v1/command/{database}` HTTP endpoint. The `GremlinQueryEngine.command()` method performs no scripting authorization check before dispatching to the Groovy engine, and the `SecureASTCustomizer` applied to the engine is explicitly documented by the developers as ineffective against RCE.\n\n## Root Cause\n\nThe `PolyglotQueryEngine` (used for JavaScript/GraalVM languages) gates script execution behind `checkScriptingPermissions()`, which requires the `UPDATE_SECURITY` database permission. `GremlinQueryEngine` has no equivalent authorization check: its `command()` method dispatches directly to the Groovy engine without verifying the caller's permission level. This gap means the scripting authorization invariant established by CVE-2026-65831 (GHSA-48qw-824m-86pr) was enforced on the JS engine path but never on the Gremlin Groovy path.\n\n```java\n// gremlin/src/main/java/com/arcadedb/gremlin/query/GremlinQueryEngine.java:67-82\n// command() creates ArcadeGremlin and calls execute() with zero authorization.\n// Compare engine/src/main/java/com/arcadedb/query/polyglot/PolyglotQueryEngine.java:148-149,\n// which calls checkScriptingPermissions() before execution.\n```\n\n## Impact\n\nAn attacker must have valid credentials for any database role (including the lowest-privilege `reader` role) on an ArcadeDB instance where the Gremlin Groovy engine has been explicitly enabled. The default engine is `java`, which is not affected. Enabling the Groovy engine requires the `UPDATE_DATABASE_SETTINGS` permission, which in the default group configuration is admin-only; however, the permission hierarchy deliberately separates `UPDATE_DATABASE_SETTINGS` from `UPDATE_SECURITY`, so custom configurations can grant the former without the latter.\n\nOnce the Groovy engine is active, any authenticated user can achieve arbitrary OS command execution as the ArcadeDB server process user through a single HTTP request. Demonstrated capabilities include host filesystem read and write, OS command execution, network access, and cross-database privilege escalation via `database.getSecurity().createUser()`.\n\nThe `SecureASTCustomizer` applied to the Groovy engine is not an effective mitigation. The developers document it as \"VULNERABLE to RCE attacks\" and state \"DO NOT RELY ON THIS FOR SECURITY\" (see `gremlin/src/main/java/com/arcadedb/gremlin/ArcadeGraph.java:706-709`). The `groovy.lang.Closure` type is whitelisted in the receiver list (line 752), which is a known escape vector for `SecureASTCustomizer`.\n\n## Proof of Concept\n\n```text\nRequirements: A running ArcadeDB server with the Gremlin module enabled, a database with\narcadedb.gremlin.engine set to \"groovy\", and valid credentials for any role (including reader).\n```\n\nStep 1: An administrator enables the Groovy engine on a target database for compatibility:\n\n```sql\nALTER DATABASE `arcadedb.gremlin.engine` `groovy`\n```\n\nStep 2: A read-only user executes an OS command via the Gremlin endpoint:\n\n```bash\ncurl -u reader1:password -X POST \\\n  \"http://localhost:2480/api/v1/command/testdb\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"language\":\"gremlin\",\"command\":\"\\\"id\\\".execute().text\"}'\n```\n\n```text\nExpected response: the output of the \"id\" command showing the UID/GID of the ArcadeDB process.\n```\n\n## Attack Chain\n\n1. **Exposure:** The `POST /api/v1/command/{database}` HTTP endpoint is the standard command interface, always available when the server module is active. It accepts a user-controlled `language` field in the JSON request body.\n2. **Control:** The attacker fully controls the `language` (set to `gremlin`) and `command` fields. The command value flows as the Groovy script body to `GremlinGroovyScriptEngine.eval()` with no sanitization, transformation, or restriction.\n3. **Path:** `PostCommandHandler.java:165,424-429` extracts the language and passes it to `database.command()`. `LocalDatabase.java:2235-2244` resolves `\"gremlin\"` via `QueryEngineManager` to `GremlinQueryEngine`. `GremlinQueryEngine.java:67-82` creates `ArcadeGremlin` and calls `execute()`. `ArcadeGremlin.java:345-387` reads the engine config and routes to `executeStatement(\"groovy\", ...)` when the Groovy engine is configured.\n4. **Guard:** No authorization check exists anywhere on this path. An exhaustive search of `gremlin/src/main/java/` for `checkScriptingPermissions`, `checkPermissionsOnDatabase`, `UPDATE_SECURITY`, and `SecurityDatabaseUser` returned zero matches in executable code. The `SecureASTCustomizer` is explicitly documented as ineffective.\n5. **Primitive:** Arbitrary Groovy/JVM code execution via `GremlinGroovyScriptEngine`. Groovy's `String.execute()` method provides direct OS command execution.\n6. **Result:** The read-only database user achieves OS-level code execution as the ArcadeDB process user, crossing from database authorization scope to host OS scope.\n\n## Bypass Evidence\n\nNo scripting authorization guard exists on the Gremlin Groovy engine path: the bypass is structural rather than a circumvention of an existing control. The `checkScriptingPermissions()` method (defined at `engine/src/main/java/com/arcadedb/query/polyglot/PolyglotQueryEngine.java:135-137`) was added as part of the CVE-2026-65831 fix, but it was implemented only in `PolyglotQueryEngine`, not in `GremlinQueryEngine`.\n\nThe fix for GHSA-wcm5-4wjm-9wj3 (\"silent Groovy fallback\") prevents the Java engine from silently falling back to Groovy in strict mode (`ArcadeGremlin.java:370-374`). It does not add any authorization check to the explicit Groovy configuration path. The GHSA-wcm5 reporter recommended adding `checkPermissionsOnDatabase(UPDATE_SECURITY)` before Groovy evaluation, but this was not implemented.\n\nThe `SecureASTCustomizer` was evaluated as a potential defense. It is explicitly documented as ineffective by the developers (`ArcadeGraph.java:706-709`), and `groovy.lang.Closure` is whitelisted in the receiver list (line 752), providing a known escape vector.\n\n## Affected Versions\n\n- Ecosystem: Maven\n- Package: `com.arcadedb:arcadedb-gremlin`\n- Confirmed affected range: `<= 26.9.1`\n- Latest release checked: 26.9.1 on Maven Central (published 2026-09-03)\n- Fix status: not fixed in any published or development version\n\nThe authorization check has never existed in `GremlinQueryEngine` in any published version. The vulnerability was introduced when `GremlinQueryEngine` was first created. HEAD (`26.10.1-SNAPSHOT`) still lacks the check. The introduction boundary cannot be precisely established from the available history, but the Gremlin module has been published since at least version 21.9.1-beta.\n\n## Suggested Fix\n\nAdd a `checkScriptingPermissions()` call (or equivalent `checkPermissionsOnDatabase(SecurityDatabaseUser.DATABASE_ACCESS.UPDATE_SECURITY)`) to `GremlinQueryEngine.command()`, matching the existing pattern in `PolyglotQueryEngine.command()`. The same check should also be applied to `GremlinQueryEngine.query()` and `GremlinQueryEngine.analyze()` to match the complete gating in `PolyglotQueryEngine`.\n\nAs an interim mitigation, ensure the Gremlin engine configuration remains at the default value of `java`. Do not set `arcadedb.gremlin.engine` to `groovy` or `auto` on instances where untrusted users have database access. Restrict the `UPDATE_DATABASE_SETTINGS` permission to prevent non-administrators from changing the engine configuration.\n\nReported by zx (GitHub: @manus-pi).","categories":["Unclassified"],"product":"com.arcadedb:arcadedb-gremlin","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/ArcadeData/arcadedb/security/advisories/GHSA-h2j4-28h8-cj5v","source_modified_at":"2026-10-05T20:05:23Z","fetched_at":"2026-10-24T05:41:33.294140Z","package":{"ecosystem":"maven","name":"com.arcadedb:arcadedb-gremlin"},"range":"<= 26.9.1"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/ArcadeData/arcadedb/security/advisories/GHSA-h2j4-28h8-cj5v","source_modified_at":"2026-10-05T20:05:23Z","fetched_at":"2026-10-24T05:41:33.294140Z","package":{"ecosystem":"maven","name":"com.arcadedb:arcadedb-gremlin"},"version":"26.10.1"}],"cvss":9.9,"severity":"Critical","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-05T20:05:23Z","modified_at":"2026-10-05T20:05:23Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/ArcadeData/arcadedb/security/advisories/GHSA-h2j4-28h8-cj5v","fetched_at":"2026-10-24T05:41:33.294140Z","source_modified_at":"2026-10-05T20:05:23Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-05T20:05:23Z","content_hash":"2dc170b61dcdc1925141f6c5a58b7e61ade3c9d760bb7f61aa21ad90daa354c2"}}
