Adminer: Path Traversal in the `sql-log` Plugin Allows Arbitrary `.sql` File Writes
An authenticated user writes arbitrary .sql files outside the web root onto the Adminer host through the unsanitized ns parameter of the sql-log plugin.
Advisory ID: TP-2026-076
Product: Adminer (single-file PHP database front-end)
Vulnerability type: Path traversal / arbitrary file write (CWE-22)
CVE: not requested
CVSS 3.1: 5.4 (Medium) · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L
Affected versions: 5.3.0 to 5.4.2
Fixed in: 5.4.3
Vendor advisory: GHSA-75xm-qwfq-9wp5
Reported: 4 July 2026
Summary
Adminer is a single-file PHP database front-end. The bundled sql-log plugin builds its log file path from the request parameter $_GET["ns"] (the PostgreSQL schema selector) without sanitization and writes every executed query into it. With no database selected, the only validation of ns is skipped, the path prefix collapses, and a value like /../../../tmp/x resolves to an absolute path outside the web root. An authenticated user who points Adminer at a database they control creates arbitrary .sql files with attacker-controlled content on the host, with no operator DB access and no admin role. turingpoint verified the flow and reported it responsibly; the vendor fixed it in 5.4.3.
Root cause
The sql-log plugin builds $this->filename without sanitization from Adminer\adminer()->database() . ($_GET["ns"] != "" ? ".$_GET[ns]" : "") . ".sql" (plugins/sql-log.php:29). It opens that path with fopen($this->filename, "a") (plugins/sql-log.php:31) and writes each executed query verbatim with fwrite($fp, $query) (plugins/sql-log.php:33). The ns parameter is only validated by set_schema(), reached solely through the block guarded by DB != "" && $_GET["ns"] !== "" (include/connect.inc.php:120). With no database selected, database() returns an empty string, that block is skipped, the prefix collapses to a single ., and ns=/../../../tmp/x resolves to an absolute path outside the web root. The attacker points Adminer at a database they control (any host on ports >= 1024 per the privileged-port guard) and logs in with their own credentials, so neither operator DB access nor an admin role is required.
Proof of Concept
On the official adminer:5.4.2 image with ADMINER_PLUGINS=sql-log, logged in to an attacker-controlled MySQL instance with an empty auth[db]:
POST /?server=mysql&username=root&sql=&ns=/../../../tmp/adminer_pwn
query=SELECT '<marker>'
-> creates /tmp/adminer_pwn.sql containing the byte-exact query text.
With no database selected the database(). prefix drops out, so the ns value acts as an absolute path and the .sql log is written outside the web root.
Impact
- Creation of arbitrary
.sqlfiles with attacker-controlled content outside the web root. - Denial of service by filling the disk, since writes are append-only.
- Second-order SQL execution if an operator later imports a poisoned
.sqlfile.
References
Is Something Like This in Your Software?
Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.
