CVE-2022-24781 (published 2022-03-24T21:15Z)

Geon is a board game based on solving questions about the Pythagorean Theorem. Malicious users can obtain the uuid from other users, spoof that uuid through the browser console and become co-owners of the target session. This issue is patched in version 1.1.0. No known workaround exists.

Severity: MEDIUM
CVSS v2 base score: 5.5 (AV:N/AC:L/Au:S/C:P/I:P/A:N)
CVSS v3 base score: 7.1, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N)

Weakness information:
cwe_id: CWE-384
cwe_name: Session Fixation
description: Authenticating a user, or otherwise establishing a new user session, without invalidating any existing session identifier gives an attacker the opportunity to steal authenticated sessions.
extended_description: 
            Such a scenario is commonly observed when:
            
               A web application authenticates a user without first invalidating the existing session, thereby continuing to use the session already associated with the user.
               An attacker is able to force a known session identifier on a user so that, once the user authenticates, the attacker has access to the authenticated session.
               The application or container uses predictable session identifiers.
	    
	    In the generic exploit of session fixation vulnerabilities, an attacker creates a new session on a web application and records the associated session identifier. The attacker then causes the victim to associate, and possibly authenticate, against the server using that session identifier, giving the attacker access to the user's account through the active session.
         
         

Fix information:
repo_url: https://github.com/math-geon/Geon
commit_details: repo_url: https://github.com/math-geon/Geon
author: Space_Interprise
author_date: 2022-01-26 22:42:45-03:00
msg: Corrigido saida de usuarios crash do backend se o client manda algo que não seja do tipo string e implementação inicial de contador de usuarios conectados

Co-Authored-By: Lobo Metalúrgico <43734867+lobometalurgico@users.noreply.github.com>
repository: repo_url: https://github.com/math-geon/Geon
repo_name: math-geon/Geon
description: Jogo de tabuleiro baseado em resolver perguntas sobre o Teorema de Pitágoras
repo_language: JavaScript
owner: math-geon
file_changes: filename: connectionhandler.js
change_type: ModificationType.MODIFY
diff: @@ -2,6 +2,7 @@ const RoomMaxPersons = 4;
 const UsableHomes = 37;
 
 const ActiveRooms = [];
+const timeouts = [];
 const ids = [];
 
 function cleaner() {
@@ -10,19 +11,25 @@ function cleaner() {
     ActiveRooms.forEach((element, index) => {
       element.Players.forEach((element2, index2) => {
         if (!ids.includes(element2.Id)) {
+          console.log('Disconnecting ' + element2.Id);
+          console.log(ActiveRooms[index].Players[index2]);
           ActiveRooms[index].Players.splice(index2, 1);
           ActiveRooms[index].Players.forEach((element3) => {
             element3.socket.emit('UserLeave', JSON.stringify({ RoomId: element.Id, UserLeaved: element2.Id }));
           });
+          element2.socket.emit('ConnectionFailed');
+          element2.socket.disconnect(0);
         }
       });
     });
     ActiveRooms.forEach((element, index) => {
       if (element.Players.length === 0) {
+        console.log('Closing Room ' + element.Id);
         ActiveRooms.splice(index, 1);
       }
     });
-    // ids = [];
+    console.log('Active Rooms: ' + ActiveRooms.length);
+    console.log('Rooms: ', ActiveRooms);
   }, 5000);
 }
 
@@ -37,11 +44,27 @@ function getRandomString(length) {
 
 function reconnect(socket) {
   console.log('Reconnect requested');
+  ActiveRooms.forEach((element, index) => {
+    element.Players.forEach((element2, index2) => {
+      if (element2.socket === socket) {
+        const timeouter = {};
+        timeouter[element2.Id] = setTimeout(() => {
+          console.log('Timeout ' + element2.Id);
+          ids.splice(ids.indexOf(element2.Id), 1);
+          timeouts.splice(timeouts.indexOf(timeouter), 1);
+          cleaner();
+        }, 15000);
+        timeouts.push(timeouter);
+        socket.broadcast.emit('Reconnecting', JSON.stringify({ RoomId: element.Id, UserId: element2.Id }));
+      }
+    });
+  });
   socket.broadcast.emit('reconnect');
   cleaner();
 }
 
 function GenerateRoom(socket, Data) {
+  if (typeof Data !== 'string') return;
   var RoomScheme = {
     Id: '',
     Players: [],
@@ -60,9 +83,19 @@ function GenerateRoom(socket, Data) {
 }
 
 function Updater(socket, Data) {
+  if (typeof Data !== 'string') return;
   Data = JSON.parse(Data);
   var UserNewId = getRandomString(12);
   console.log('Updating User ' + Data.oldUserId);
+
+  timeouts.forEach((element, index) => {
+    if (element[Data.oldUserId]) {
+      console.log('User Reconnected');
+      clearTimeout(element[Data.oldUserId]);
+      timeouts.splice(index, 1);
+    }
+  });
+
   ActiveRooms.forEach((element, index) => {
     element.Players.forEach((element2, index2) => {
       if (element2.Id === Data.oldUserId) {
@@ -81,6 +114,12 @@ function verifyer(socket, executer, Data, UserIn) {
   if (UserIn === undefined) {
     UserIn = true;
   }
+  if (typeof Data !== 'string') {
+    socket.emit('ConnectionFailed');
+    socket.disconnect(0);
+    socket.destroy();
+    return;
+  }
   Data = JSON.parse(Data);
   var RoomContainsUser = false;
   var RoomExists = false;
@@ -130,6 +169,7 @@ function LogUserIn(socket, Data) {
       ActiveRooms[index].Players.forEach((element2) => {
         element2.socket.emit('NewUser', JSON.stringify({ RoomId: element.Id, EnteredUserId: UserId }));
         socket.emit('NewUser', JSON.stringify({ RoomId: element.Id, EnteredUserId: element2.Id }));
+        socket.emit('UserNameInformation', JSON.stringify({ RoomId: element.Id, UserId: element2.Id, NickName: element2.Name }));
       });
     }
   });
@@ -170,7 +210,8 @@ function RegisterUserName(socket, Data) {
         } else if (element2.Id === Data.UserId) {
           ActiveRooms[index].Players[index2].Name = userName;
         }
-        socket.emit('UserNameInformation', JSON.stringify({ RoomId: element.Id, UserId: element2.Id, NickName: element2.Name }));
+        console.log('Sending UserName to user: ' + element2.Id + ' ' + userName);
+        element2.socket.emit('UserNameInformation', JSON.stringify({ RoomId: element.Id, UserId: Data.UserId, NickName: userName }));
       });
     }
   });

programming_language: TypeScript
filename: Geon.html
change_type: ModificationType.MODIFY
diff: @@ -77,12 +77,13 @@
                 <div class="CreditsScreen" style="display: none">
                     <button class="BackToMainMenuFromCredits" onclick="BackToMainMenu('Credits')">Voltar</button>
                     <h1 class="CreditsHeader">Créditos:</h1>
-                    <span><a href="https://github.com/emanuelfranklyn" target="_blank" >Emanuel <a target="_blank" href="https://www.talesgardem.com.br/">Franklyn</a></a>: <a target="_blank" class="EasterEgg" href="https://www.youtube.com/watch?v=KvJHqk4IgGE">Instrutor de programação do jogo.</a></span><br/>
+                    <span><a target="_blank" href="https://github.com/emanuelfranklyn">Emanuel <a target="_blank" href="https://www.talesgardem.com.br/">Franklyn</a></a>: <a target="_blank" class="EasterEgg" href="https://www.youtube.com/watch?v=KvJHqk4IgGE">Instrutor de programação do jogo.</a></span><br/>
                     <span><a target="_blank" href="https://twitter.com/wTOPwOficial">Emmanuel Florêncio</a>: Game Designer, criação de regras.</span><br/>
                     <span><a target="_blank" href="https://www.instagram.com/_filipin_s/">Filipe Santos</a>: Confecção de imagens do jogo.</span><br/>
                     <span><a target="_blank" href="https://www.instagram.com/Gabriel._moreira_.05/">Gabriel Moreira</a>: Programação do jogo, revisão de erros ortográficos.</span><br/>
                     <span><a target="_blank" href="https://www.instagram.com/_joaofrancisco.f/">João Francisco</a>: Revisão de erros ortográficos, criação de regras.</span><br/>
                     <span><a target="_blank" href="https://www.instagram.com/ricardopfilho/">Ricardo Filho</a>: Projeto Escrito, apresentador.</span><br/>
+                    <span><a target="_blank" href="https://github.com/LoboMetalurgico">Lobo </a> <a target="_blank" href="https://beta.allonsve.com/">Metalurgico</a>: Contribuidor, progamação</span><br/>
                 </div>
                 <div class="ConnectingScreen" style="display: none">
                     <h1 class="ConnectingText">Conectando ao servidor...</h1>
@@ -135,7 +136,7 @@
                         </form>      
                     </div>
                     <div class="PlayerInTheRoom">
-                        <p class="PlayerInTheRoomHeader">Jogadores na sala:</p>
+                        <p class="PlayerInTheRoomHeader">Jogadores na sala: (0/4)</p>
                         <div class="Players"></div>
                     </div>
                     <div class="Dice" onclick="RunDice()" style="background-image: url('/staGeon/resources/DiceIcon.png');">

programming_language: HTML
filename: main.js
change_type: ModificationType.MODIFY
diff: @@ -76,27 +76,57 @@ function Loaded() {
 
   socket.on('NewUser', (Data) => {
     Data = JSON.parse(Data);
-    CreateUsers(Data.EnteredUserId);
+    if (!document.getElementById(Data.EnteredUserId)) {
+      CreateUsers(Data.EnteredUserId);
+    }
     AddUserPawn();
     const Playrs = [...document.getElementsByClassName('Players')[0].children];
     Playrs[0].style.webkitTextStroke = '1px #FFF';
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
   });
 
   socket.on('UserNameInformation', (Data) => {
+    console.log('Received UserNameInformation: ' + Data);
     Data = JSON.parse(Data);
     const Players = [...document.getElementsByClassName('Players')[0].children];
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
     Data.NickName = Data.NickName.split('>').join(' ');
     Data.NickName = Data.NickName.split('<').join(' ');
     Data.NickName = Data.NickName.split('/').join(' ');
+    var userFounded = false;
+    console.log(Data.UserId);
     Players.forEach((element) => {
       if (element.id === Data.UserId) {
-        playingPlayers.push({
-          id: Data.UserId,
-          name: Data.NickName,
-        });
-        element.innerHTML = Data.UserId === UserId ? `Você(${Data.NickName})` : Data.NickName;
+        userFounded = true;
+        if (playingPlayers.find((object) => { return object.id === Data.UserId; })) {
+          const player = playingPlayers.find((object) => { return object.id === Data.UserId; });
+          playingPlayers[player.index].name = Data.NickName;
+          console.log('Updated user on the list: ' + JSON.stringify(playingPlayers));
+        } else {
+          playingPlayers.push({
+            id: Data.UserId,
+            name: Data.NickName,
+            index: playingPlayers.length,
+          });
+          console.log('Added new user to the list: ' + JSON.stringify(playingPlayers));
+        }
+        element.innerHTML = Data.UserId === UserId ? `Você (${Data.NickName})` : Data.NickName;
       }
     });
+    if (!userFounded) {
+      playingPlayers.push({
+        id: Data.UserId,
+        name: Data.NickName,
+        index: playingPlayers.length,
+      });
+      console.log('Created new user to the list: ' + JSON.stringify(playingPlayers));
+      console.log(Data.UserId);
+      const Player = document.createElement('div');
+      Player.className = 'PlayerWrapper';
+      Player.id = Data.UserId;
+      Player.innerHTML = Data.UserId === UserId ? `Você (${Data.NickName})` : Data.NickName;
+      document.getElementsByClassName('Players')[0].appendChild(Player);
+    }
   });
 
   socket.on('UserLeave', (Data) => {
@@ -104,6 +134,11 @@ function Loaded() {
     if (document.getElementById(Data.UserLeaved)) {
       document.getElementById(Data.UserLeaved).remove();
     }
+    if (playingPlayers.find((object) => { return object.id === Data.UserId; })) {
+      playingPlayers.splice(playingPlayers.find((object) => { return object.id === Data.UserId; }).index, 1);
+    }
+    document.getElementsByClassName('Players')[0].children[0].style.webkitTextStroke = '1px #FFF';
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
     AddUserPawn();
   });
 
@@ -122,6 +157,7 @@ function Loaded() {
     Playrs.forEach((element, index) => {
       element.remove();
     });
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
     playingPlayers = [];
   });
 
@@ -140,6 +176,7 @@ function Loaded() {
     Playrs.forEach((element, index) => {
       element.remove();
     });
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
     playingPlayers = [];
     document.getElementById('C0').style.top = 0 + 'vw';
     document.getElementById('C0').style.left = 0 + 'vw';
@@ -159,6 +196,21 @@ function Loaded() {
     console.log('My new ID is: ' + id);
   });
 
+  socket.on('Reconnecting', (Data) => {
+    Data = JSON.parse(Data);
+
+    if (Data.RoomId !== CurrentRoom) return;
+
+    const Players = [...document.getElementsByClassName('Players')[0].children];
+
+    Players.forEach((element) => {
+      if (element.id === Data.UserId) {
+        element.innerHTML = 'Reconnecting...';
+      }
+    });
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
+  });
+
   socket.on('reconnect', () => {
     console.log('Reconnect!');
     socket.emit('Reconnector', JSON.stringify({ oldUserId: UserId }));
@@ -172,9 +224,14 @@ function Loaded() {
     Players.forEach((element, index) => {
       if (element.id === Data.oldUserId) {
         element.id = Data.newUserId;
-        playingPlayers.find((object) => { return object.id === Data.oldUserId; }).id = Data.newUserId;
+        const player = playingPlayers.find((object) => { return object.id === Data.oldUserId; });
+        if (player) {
+          playingPlayers[player.index].id = Data.newUserId;
+          console.log('changed id actual user NewId:' + Data.newUserId + ' OldId:' + Data.oldUserId + ' Index:' + player.index + ' Full Object:' + JSON.stringify(playingPlayers[player.index]));
+        }
       }
     });
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
     UserId = Data.newUserId;
   });
 
@@ -184,9 +241,13 @@ function Loaded() {
     Players.forEach((element, index) => {
       if (element.id === Data.OldId) {
         element.id = Data.NewId;
-        playingPlayers.find((object) => { return object.id === Data.OldId; }).id = Data.NewId;
+        const checkPlayer = playingPlayers.find((object) => { return object.id === Data.OldId; });
+        if (!checkPlayer) return;
+        playingPlayers[checkPlayer.index].id = Data.NewId;
+        console.log('Updated some user ID: ' + Data.NewId + ' OldId:' + Data.OldId + ' Index:' + checkPlayer.index + ' Full Object:' + JSON.stringify(playingPlayers[checkPlayer.index]));
       }
     });
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
   });
 
   socket.on('ConnectionFailed', () => {
@@ -243,11 +304,13 @@ function Loaded() {
     }
     document.getElementById('C' + UserIndex.toString()).style.top = (Math.floor(Collumn - 1) * 7) + 'vw';
     document.getElementById('C' + UserIndex.toString()).style.left = (Math.floor(Row) * 7) + 'vw';
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
   });
   socket.on('QuestionToAnswer', (Data) => {
     questionPopupToRespond = true;
     Data = JSON.parse(Data);
     document.getElementsByClassName('QuestionInput')[0].id = 'popup';
+    document.getElementsByClassName('QuestionTextInput')[0].value = '';
     setTimeout(() => { document.getElementsByClassName('QuestionInput')[0].id = ''; }, 300);
     document.getElementsByClassName('QuestionInput')[0].style.display = 'block';
     document.getElementsByClassName('SideA')[0].innerHTML = Data.SideA;
@@ -267,8 +330,12 @@ function Loaded() {
     Data = JSON.parse(Data);
     const Playrs = [...document.getElementsByClassName('Players')[0].children];
     Playrs.forEach((element, index) => {
-      var userName = playingPlayers.find((object) => { return object.id === element.id; }).name || 'Aguardando...';
-      if (element.id === UserId) userName = `Você(${userName})`;
+      var userName = playingPlayers.find((object) => { return object.id === element.id; });
+      console.log('UserName:' + userName.name);
+      console.log('object:' + JSON.stringify(userName));
+      if (!userName) { userName = 'Aguardando...'; } else { userName = userName.name; }
+      console.log('UserName:' + userName.name);
+      if (element.id === UserId) userName = `Você (${userName})`;
       if (index === Data.Turn) {
         element.style.webkitTextStroke = '1px #FFFFFF';
         element.innerHTML = userName + ' <--';
@@ -286,6 +353,7 @@ function Loaded() {
         element.style.color = '#FFAAFF';
       }
     });
+    document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
   });
 
   socket.on('UserNameAlreadyExists', () => {
@@ -416,7 +484,6 @@ function RunDice() {
 
 function AddUserPawn() {
   const Players = [...document.getElementsByClassName('Players')[0].children];
-  Players.length -= 1;
   if (Players.length === 2) {
     document.getElementById('C0').style.display = 'block';
     document.getElementById('C1').style.display = 'block';
@@ -438,6 +505,7 @@ function AddUserPawn() {
     document.getElementById('C2').style.display = 'none';
     document.getElementById('C3').style.display = 'none';
   }
+  document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
 }
 
 function HipotenusaResulver() {
@@ -478,6 +546,7 @@ function Win() {
   Playrs.forEach((element, index) => {
     element.remove();
   });
+  document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
   playingPlayers = [];
   document.getElementById('C0').style.top = 0 + 'vw';
   document.getElementById('C0').style.left = 0 + 'vw';
@@ -505,6 +574,7 @@ function Lose() {
   Playrs.forEach((element, index) => {
     element.remove();
   });
+  document.getElementsByClassName('PlayerInTheRoomHeader')[0].innerHTML = `Jogadores na sala: (${playingPlayers.length}/4)`;
   playingPlayers = [];
   document.getElementById('C0').style.top = 0 + 'vw';
   document.getElementById('C0').style.left = 0 + 'vw';

programming_language: JavaScript
filename: styles.css
change_type: ModificationType.MODIFY
diff: @@ -1080,7 +1080,6 @@ body {
     height: 5vw;
     margin-left: 12vw;
     font-size: 3vw;
-    align: right;
 }
 .SideB {
     position: absolute;

programming_language: CSS