GHSA-6vfx-w7q3-42gq

Suggest an improvement
Source
https://github.com/advisories/GHSA-6vfx-w7q3-42gq
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-6vfx-w7q3-42gq/GHSA-6vfx-w7q3-42gq.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-6vfx-w7q3-42gq
Aliases
  • CVE-2026-96633
Published
2026-10-30T22:52:58Z
Modified
2026-10-30T23:00:04Z
Severity
  • 8.1 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H CVSS Calculator
Summary
AdminNeo SQLite Database-List Drop Arbitrary File Deletion
Details

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.
Database specific
{
    "cwe_ids":  [
        "CWE-73"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-10-30T22:52:58Z",
    "nvd_published_at":  null,
    "severity":  "HIGH"
}
References

Affected packages

Packagist / adminneo-org/adminneo

Package

Name
adminneo-org/adminneo
Purl
pkg:composer/adminneo-org/adminneo

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
5.5.1

Affected versions

v4.*
v4.2.0
v4.2.1
v4.2.2
v4.2.3
v4.2.4
v4.2.5
v4.3.0
v4.3.1
v4.4.0
v4.5.0
v4.6.0
v4.6.1
v4.6.2
v4.6.3
v4.7.0
v4.7.1
v4.7.2
v4.7.3
v4.7.4
v4.7.5
v4.7.6
v4.7.7
v4.7.8
v4.7.9
v4.8.0
v4.8.1
v4.8.2
v4.9.0
v4.9.1
v4.9.2
v4.9.3
v4.9.4
v4.10.0
v4.11.0
v4.12.0
v4.13.0
v4.14.0
v4.15.0
v4.16.0
v4.16.1
v4.17.0
v4.17.1
v4.17.2
v5.*
v5.0.0-rc
v5.0.0
v5.1.0
v5.1.1
v5.2.0
v5.2.1
v5.3.0
v5.4.0
v5.4.1
v5.5.0

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-6vfx-w7q3-42gq/GHSA-6vfx-w7q3-42gq.json"