Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

CVE-2026-75609 — EGroupware: post-authentication open redirect via login.php?phpgw_forward=

  • Advisory: GHSA-7pj6-f27j-wmcw · CVE-2026-75609
  • Affected: egroupware/egroupware < 26.7.20260710 and < 23.1.20260710 · Fixed: 26.7.20260710 / 23.1.20260710
  • Severity: Medium · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N = 6.1 · CWE-601
  • Status: publicly disclosed and fixed. Reported by Pig-Tail through coordinated disclosure.

Summary

login.php reads the attacker-supplied phpgw_forward parameter and, after a successful login, redirects the browser to it. Api\Session::link() is supposed to keep redirects local by prefixing the configured webserver_url — but its guard only applies that prefix when the target does not already contain "$webserver_url/" as a substring:

// api/src/Session.php::link()
if (($url[0] != '/' || $webserver_url != '/') &&
    (!$webserver_url || strpos($url, $webserver_url.'/') === false)) { /* prefix webserver_url */ }

With the default webserver_url = /egroupware, an absolute attacker URL that simply embeds that literal substring — https://evil.com/egroupware/ — makes strpos(...) !== false, the guard is skipped, and the URL is used verbatim as the Location. The app-prefix at the top of link() ($url = $app.'/'.$url) is also skipped, because currentapp == 'login' on login.php, so the absolute URL is not mangled either.

Result: Location: https://evil.com/egroupware/?cd=yes.

Alternate vector: on deployments with webserver_url of / or empty (reverse-proxy / docker roots), phpgw_forward=//evil.com/ is protocol-relative and passes the same guard.

Reachability

  1. Attacker sends the victim …/login.php?phpgw_forward=https://evil.com/egroupware/.
  2. Victim authenticates — the login form action carries phpgw_forward (api/src/Framework/Login.php), so it survives the POST, and parseForward also reads it from POST.
  3. parseForward → forward = https://evil.com/egroupware/, extra_vars = cd=yes.
  4. redirect_link → link(): app prefix skipped (currentapp=login); webserver_url prefix skipped because the URL contains /egroupware/; enforce_ssl, where set, only rewrites http: → https: and preserves the host — there is no host allowlist anywhere on the path.
  5. api/src/Framework.php:212 → Header("Location: https://evil.com/egroupware/?cd=yes").

Impact

Credential phishing and OAuth/redirect-chain abuse: a link to the legitimate EGroupware host lands the user on an attacker page immediately after login, defeating the trust signal of the real login domain. On SSO installs — where Api\Auth::login() returns a session with no credential entry — the redirect can fire with no interactive login at all.

This is redirect-only, not XSS: the value reaches a Location: header, and javascript: / data: schemes in Location are ignored by modern browsers.

PoC

poc/open_redirect_poc.php reproduces Session::link()'s guard verbatim and prints the Location header Framework::redirect() would emit, for both attack vectors plus two negative controls. Fully offline — no network traffic and no EGroupware installation:

php poc/open_redirect_poc.php     # PHP 8.0+
webserver_url phpgw_forward Location: Result
/egroupware https://evil.com/egroupware/ https://evil.com/egroupware/ off-site
/egroupware https://evil.com/ /egroupwarehttps://evil.com/ stays local
`` (root install) //evil.com/ //evil.com/ off-site
/egroupware /index.php?menuaction=home /egroupware/index.php?menuaction=home stays local

The negative controls matter: the guard is not simply absent. It works — right up until the attacker's URL contains the configured path as a substring, which the attacker fully controls.

Fix

In Session::link() / the login forward handling, reject any phpgw_forward that is absolute or protocol-relative (//), or validate its host against an allowlist containing only the configured webserver_url host. Do not use strpos(webserver_url.'/') as a locality test — the attacker owns the rest of the URL. Canonicalize to a path-only forward.