- Package
- adminneo-org/adminneo
- Fixed in
- 5.5.1
- Published
- Fri 30 Oct 2026, 22:52 Lisbon
- Installs on Packagist
- 291
In plain words
An authenticated AdminNeo user in SQLite mode with a valid CSRF token can use the database-list drop action to delete files that are not SQLite databases. The deletion is limited to files the PHP process has permission to delete.
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| adminneo-org/adminneo | < 5.5.1 | 5.5.1 |
Am I affected?
Check composer.lock for adminneo-org/adminneo below 5.5.1; 5.5.1 is fixed. If affected, check whether AdminNeo is used in SQLite mode and authenticated users can access the database-list drop action. The flaw requires a valid CSRF token and permission for the PHP process to delete the target file.
What to do
Check which version you have, then update to a fixed release for your branch (5.5.1):
composer show adminneo-org/adminneo | grep versions
composer update adminneo-org/adminneo
If your version constraint in composer.json keeps you below a fixed release, raise it first. Whether your configuration is affected, and the exact version ranges, are in the advisory itself.
If you cannot update yet
The advisory suggests validating deletion targets: require a .db, .sdb or .sqlite filename extension, restrict db[] values to databases returned by AdminNeo's listing, and resolve paths to reject traversal outside the expected SQLite database directory. It also suggests considering deletion only of the currently selected database path.
The advisory's description
Summary
AdminNeo allows an authenticated SQLite-mode user to delete arbitrary files writable by the PHP process through the database-list drop action. The SQLite driver validates file extensions when creating or renaming databases, but the database-list drop path passes submitted db[] values directly to unlink().
Originally reported by @slpoiuewalspoid-dotcom in Adminer: https://github.com/vrana/adminer/security/advisories/GHSA-6pg3-chwq-wgqc
Impact
An authenticated AdminNeo SQLite user with a valid CSRF token can submit an arbitrary relative file path in the database-list db[] field. If the PHP process can delete that path, AdminNeo unlinks it even when the file is not a SQLite database.
Root Cause
The database-list page accepts selected database names from the POST body and calls drop_databases():
if ($_POST["db"]) {
queries_redirect(substr(ME, 0, -1), lang('Databases have been dropped.'), drop_databases($_POST["db"]));
}
In SQLite mode, drop_databases() attaches an in-memory database and unlinks each submitted path directly:
function drop_databases($databases): bool
{
Connection::get()->close(); // to unlock file, doesn't work in PDO on Windows
foreach ($databases as $db) {
if (!@unlink($db)) {
Connection::get()->setError(lang('File exists.'));
return false;
}
}
return true;
}
The same SQLite driver protects create and rename operations with check_sqlite_name(), which requires a .db, .sdb, or .sqlite extension:
function check_sqlite_name($name) {
// avoid creating PHP files on unsecured servers
$extensions = "db|sdb|sqlite";
if (!preg_match("~^[^\\0]*\\.($extensions)\$~", $name)) {
Connection::get()->setError(lang('Please use one of the extensions %s.', str_replace("|", ", ", $extensions)));
return false;
}
return true;
}
The drop path does not apply an equivalent extension check or bind the submitted path to a known listed SQLite database.
POC
1. Start a complete AdminNeo application
Build and start AdminNeo from the audited source using the official standalone Docker image shape. The validation image copies the audited source tree into /var/www/html and starts Nginx HTTP server.
Log in through the SQLite entry point using a SQLite database path poc.sqlite.
2. Prepare a writable target file
Place a non-database file under a location writable by the AdminNeo PHP process. The validation used:
/var/www/html/poc-delete-target.txt
The same directory also contained a SQLite database:
/var/www/html/poc.sqlite
3. Submit the database-list drop request
Use the authenticated session and live CSRF token, but submit the non-database path as the selected database:
POST /?sqlite=&username= HTTP/1.1
Host: <adminer-host>
Cookie: <authenticated-session>
Content-Type: application/x-www-form-urlencoded
db[]=/var/www/html/poc-delete-target.txt&all=&drop=Drop&token=<live-csrf-token>
The request reaches drop_databases($_POST["db"]), and SQLite drop_databases() calls unlink() on the submitted non-database path.
After the request, the poc-delete-target.txt was deleted.
Suggested Fix
Apply the same target validation used by SQLite create/rename before deleting database files. At minimum:
- Reject paths whose basename does not end in
.db,.sdb, or.sqlite. - Bind
db[]values to databases returned by AdminNeo's database listing instead of trusting submitted paths. - Resolve paths before deletion and reject traversal outside the expected SQLite database directory.
- Consider requiring the current selected database path for delete operations instead of accepting arbitrary submitted file paths.
Weakness: CWE-73 External Control of File Name or Path. Reported by slpoiuewalspoid-dotcom, vrana.
The advisory
- GitHub Advisory Database: GHSA-6vfx-w7q3-42gq, with who is affected and how
- OSV.dev: GHSA-6vfx-w7q3-42gq
- All advisories for adminneo-org/adminneo