CVE-2021-41176: Cross-Site Request Forgery (CSRF) (CWE-352)
value: Pterodactyl is an open-source game server management panel built with PHP 7, React, and Go. In affected versions of Pterodactyl a malicious user can trigger a user logout if a signed in user visits a malicious website that makes a request to the Panel's sign-out endpoint. This requires a targeted attack against a specific Panel instance, and serves only to sign a user out. **No user details are leaked, nor is any user data affected, this is simply an annoyance at worst.** This is fixed in version 1.6.3.
CWE description: 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.
CVSS v3 base score: 4.3
Published: 2021-10-25T17:15Z
Repository: https://github.com/pterodactyl/panel
Language: PHP
Files changed: SECURITY.md, resources/scripts/components/NavigationBar.tsx, routes/auth.php
Commit date: 2021-10-23 13:00:21 -0700
Commit message: (security) use POST for logout rather than GET

see https://github.com/pterodactyl/panel/security/advisories/GHSA-m49f-hcxp-6hm6

Diff: diff --git a/SECURITY.md b/SECURITY.md
index a00178658..171a0cb13 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -9,10 +9,10 @@ The following versions of Pterodactyl are receiving active support and maintenan
 | 0.7.x | daemon@0.6.x | :x: |
 
 
 ## Reporting a Vulnerability
 
-Please reach out directly to any project team member on Discord when reporting a security vulnerability, or you can send an email to `dane [ät] pterodactyl.io`.
+Please reach out directly to any project team member on Discord when reporting a security vulnerability, or you can send an email to `dane@pterodactyl.io`.
 
 We make every effort to respond as soon as possible, although it may take a day or two for us to sync internally and determine the severity of the report and its impact. Please, _do not_ use a public facing channel or GitHub issues to report sensitive security issues.
 
 As part of our process, we will create a security advisory for the affected versions and disclose it publicly, usually two to four weeks after a releasing a version that addresses it.
diff --git a/resources/scripts/components/NavigationBar.tsx b/resources/scripts/components/NavigationBar.tsx
index 64845591a..9500d215d 100644
--- a/resources/scripts/components/NavigationBar.tsx
+++ b/resources/scripts/components/NavigationBar.tsx
@@ -5,10 +5,13 @@ import { faCogs, faLayerGroup, faSignOutAlt, faUserCircle } from '@fortawesome/f
 import { useStoreState } from 'easy-peasy';
 import { ApplicationStore } from '@/state';
 import SearchContainer from '@/components/dashboard/search/SearchContainer';
 import tw, { theme } from 'twin.macro';
 import styled from 'styled-components/macro';
+import http from '@/api/http';
+import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
+import { useState } from 'react';
 
 const Navigation = styled.div`
     ${tw`w-full bg-neutral-900 shadow-md overflow-x-auto`};
     
     & > div {
@@ -25,11 +28,11 @@ const Navigation = styled.div`
 `;
 
 const RightNavigation = styled.div`
     ${tw`flex h-full items-center justify-center`};
     
-    & > a, & > .navigation-link {
+    & > a, & > button, & > .navigation-link {
         ${tw`flex items-center h-full no-underline text-neutral-300 px-6 cursor-pointer transition-all duration-150`};
         
         &:active, &:hover {
             ${tw`text-neutral-100 bg-black`};
         }
@@ -41,13 +44,23 @@ const RightNavigation = styled.div`
 `;
 
 export default () => {
     const name = useStoreState((state: ApplicationStore) => state.settings.data!.name);
     const rootAdmin = useStoreState((state: ApplicationStore) => state.user.data!.rootAdmin);
+    const [ isLoggingOut, setIsLoggingOut ] = useState(false);
+
+    const onTriggerLogout = () => {
+        setIsLoggingOut(true);
+        http.post('/auth/logout').finally(() => {
+            // @ts-ignore
+            window.location = '/';
+        });
+    };
 
     return (
         <Navigation>
+            <SpinnerOverlay visible={isLoggingOut} />
             <div css={tw`mx-auto w-full flex items-center`} style={{ maxWidth: '1200px', height: '3.5rem' }}>
                 <div id={'logo'}>
                     <Link to={'/'}>
                         {name}
                     </Link>
@@ -63,13 +76,13 @@ export default () => {
                     {rootAdmin &&
                     <a href={'/admin'} rel={'noreferrer'}>
                         <FontAwesomeIcon icon={faCogs}/>
                     </a>
                     }
-                    <a href={'/auth/logout'}>
+                    <button onClick={onTriggerLogout}>
                         <FontAwesomeIcon icon={faSignOutAlt}/>
-                    </a>
+                    </button>
                 </RightNavigation>
             </div>
         </Navigation>
     );
 };
diff --git a/routes/auth.php b/routes/auth.php
index 2e9a01eaf..0acd9fded 100644
--- a/routes/auth.php
+++ b/routes/auth.php
@@ -46,6 +46,6 @@ Route::group(['middleware' => 'guest'], function () {
 |--------------------------------------------------------------------------
 |
 | Endpoint: /auth
 |
 */
-Route::get('/logout', 'LoginController@logout')->name('auth.logout')->middleware('auth');
+Route::post('/logout', 'LoginController@logout')->name('auth.logout')->middleware('auth', 'csrf');

Vulnerable code: Please reach out directly to any project team member on Discord when reporting a security vulnerability, or you can send an email to `dane [ät] pterodactyl.io`.
    & > a, & > .navigation-link {
                    <a href={'/auth/logout'}>
                    </a>
Route::get('/logout', 'LoginController@logout')->name('auth.logout')->middleware('auth');
Fixed code: Please reach out directly to any project team member on Discord when reporting a security vulnerability, or you can send an email to `dane@pterodactyl.io`.
import http from '@/api/http';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import { useState } from 'react';
    & > a, & > button, & > .navigation-link {
    const [ isLoggingOut, setIsLoggingOut ] = useState(false);

    const onTriggerLogout = () => {
        setIsLoggingOut(true);
        http.post('/auth/logout').finally(() => {
            // @ts-ignore
            window.location = '/';
        });
    };
            <SpinnerOverlay visible={isLoggingOut} />
                    <button onClick={onTriggerLogout}>
                    </button>
Route::post('/logout', 'LoginController@logout')->name('auth.logout')->middleware('auth', 'csrf');