CVE-2021-41273 (PHP, published 2021-11-17T20:15Z)
Description: value: Pterodactyl is an open-source game server management panel built with PHP 7, React, and Go. Due to improperly configured CSRF protections on two routes, a malicious user could execute a CSRF-based attack against the following endpoints: Sending a test email and Generating a node auto-deployment token. At no point would any data be exposed to the malicious user, this would simply trigger email spam to an administrative user, or generate a single auto-deployment token unexpectedly. This token is not revealed to the malicious user, it is simply created unexpectedly in the system. This has been addressed in release `1.6.6`. Users may optionally manually apply the fixes released in v1.6.6 to patch their own systems.
Severity: MEDIUM (CVSS v3 base score: 4.3)
Weakness: CWE-352 \u2014 Cross-Site Request Forgery (CSRF)
The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
Repository: https://github.com/pterodactyl/panel
Files changed:
app/Http/Kernel.php
app/Http/Middleware/Api/AuthenticateKey.php
app/Http/Middleware/VerifyCsrfToken.php
resources/scripts/api/http.ts
resources/views/admin/nodes/view/configuration.blade.php
resources/views/admin/settings/mail.blade.php
routes/admin.php
Commit message: Add consistent CSRF token verification to API endpoints; address security concern with non-CSRF protected endpoints


Vulnerable code:
            throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']);
     * The URIs that should be excluded from CSRF verification.
     * @var array
    protected $except = [
        'remote/*',
        'daemon/*',
        'api/*',
    ];
        'X-CSRF-Token': (window as any).X_CSRF_TOKEN as string || '',
        $.getJSON('{{ route('admin.nodes.view.configuration.token', $node->id) }}').done(function (data) {
                    method: 'GET',
                    headers: { 'X-CSRF-Token': $('input[name="_token"]').val() }
    Route::get('/mail/test', 'Settings\MailController@test')->name('admin.settings.mail.test');
    Route::get('/view/{node}/settings/token', 'NodeAutoDeployController')->name('admin.nodes.view.configuration.token');

Fixed code:
            VerifyCsrfToken::class,
            VerifyCsrfToken::class,
use Illuminate\Support\Facades\Session;
            throw new HttpException(401, 'A bearer token or valid user session cookie must be provided to access this endpoint.', null, ['WWW-Authenticate' => 'Bearer']);
use Closure;
use Pterodactyl\Models\ApiKey;
     * The URIs that should be excluded from CSRF verification. These are
     * never hit by the front-end, and require specific token validation
     * to work.
     * @var string[]
    protected $except = ['remote/*', 'daemon/*'];

    /**
     * Manually apply CSRF protection to routes depending on the authentication
     * mechanism being used. If the API request is using an API key that exists
     * in the database we can safely ignore CSRF protections, since that would be
     * a manually initiated request by a user or server.
     *
     * All other requests should go through the standard CSRF protections that
     * Laravel affords us. This code will be removed in v2 since we have switched
     * to using Sanctum for the API endpoints, which handles that for us automatically.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     *
     * @throws \Illuminate\Session\TokenMismatchException
     */
    public function handle($request, Closure $next)
    {
        $key = $request->attributes->get('api_key');

        if ($key instanceof ApiKey && $key->exists) {
            return $next($request);
        }

        return parent::handle($request, $next);
    }
http.interceptors.request.use(req => {
    const cookies = document.cookie.split(';').reduce((obj, val) => {
        const [ key, value ] = val.trim().split('=').map(decodeURIComponent);

        return { ...obj, [key]: value };
    }, {} as Record<string, string>);

    req.headers['X-XSRF-TOKEN'] = cookies['XSRF-TOKEN'] || 'nil';

    return req;
});

        $.ajax({
            method: 'POST',
            url: '{{ route('admin.nodes.view.configuration.token', $node->id) }}',
            headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
        }).done(function (data) {
                    method: 'POST',
                    headers: { 'X-CSRF-TOKEN': $('input[name="_token"]').val() }
    Route::post('/mail/test', 'Settings\MailController@test')->name('admin.settings.mail.test');
    Route::post('/view/{node}/settings/token', 'NodeAutoDeployController')->name('admin.nodes.view.configuration.token');