CVE-2024-21641 (PHP, published 2024-01-05T21:15Z)
Description: value: Flarum is open source discussion platform software. Prior to version 1.8.5, the Flarum `/logout` route includes a redirect parameter that allows any third party to redirect users from a (trusted) domain of the Flarum installation to redirect to any link. For logged-in users, the logout must be confirmed. Guests are immediately redirected. This could be used by spammers to redirect to a web address using a trusted domain of a running Flarum installation. The vulnerability has been fixed and published as flarum/core v1.8.5. As a workaround, some extensions modifying the logout route can remedy this issue if their implementation is safe.
Severity: nan (CVSS v3 base score: 4.7)
Weakness: CWE-601 \u2014 URL Redirection to Untrusted Site ('Open Redirect')
A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
Repository: https://github.com/flarum/flarum-core
Files changed:
src/Forum/Controller/LogOutController.php
Commit message: [1.x] fix: Logout controller allows open redirects (#3948)

* fix: prevent open redirects on logout controller

* use clearer config key

* cast url as string, reinstate guest redirect

* clean up a little

* simplify

* return Uri

* resolve ternary always true

* simplify some more

* remove extra newline

* handle malformed uri

* chore: requested changes

Vulnerable code:
        UrlGenerator $url
        $url = Arr::get($request->getQueryParams(), 'return', $this->url->to('forum')->base());
        // If there is no user logged in, return to the index.
            return new RedirectResponse($url);
            $return = Arr::get($request->getQueryParams(), 'return');

                ->with('url', $this->url->to('forum')->route('logout').'?token='.$csrfToken.($return ? '&return='.urlencode($return) : ''));
        $response = new RedirectResponse($url);

Fixed code:
use Flarum\Foundation\Config;
use Laminas\Diactoros\Uri;
    /**
     * @var Config
     */
    protected $config;

     * @param Config $config
        UrlGenerator $url,
        Config $config
        $this->config = $config;
        $base = $this->url->to('forum')->base();
        $returnUrl = Arr::get($request->getQueryParams(), 'return');
        $return = $this->sanitizeReturnUrl((string) $returnUrl, $base);
        // If there is no user logged in, return to the index or the return url if it's set.
            return new RedirectResponse($return);
                ->with('url', $this->url->to('forum')->route('logout') . '?token=' . $csrfToken . ($returnUrl ? '&return=' . urlencode($return) : ''));
        $response = new RedirectResponse($return);

    protected function sanitizeReturnUrl(string $url, string $base): Uri
    {
        if (empty($url)) {
            return new Uri($base);
        }

        try {
            $parsedUrl = new Uri($url);
        } catch (\InvalidArgumentException $e) {
            return new Uri($base);
        }

        if (in_array($parsedUrl->getHost(), $this->getAllowedRedirectDomains())) {
            return $parsedUrl;
        }

        return new Uri($base);
    }

    protected function getAllowedRedirectDomains(): array
    {
        $forumUri = $this->config->url();

        return array_merge(
            [$forumUri->getHost()],
            $this->config->offsetGet('redirectDomains') ?? []
        );
    }