2.0 Comment récupérer le aesKey qui décode un ticket utilisé par le RawDataMessage ?

Inscrit
31 Janvier 2021
Messages
5
Reactions
1
#1
Bonjour,

Je suis depuis quelques temps ce forum où j'ai pu trouver des pépites en termes d'informations sur le reverse de Dofus2, et qui m'ont beaucoup aidé dans la fabrication d'un bot Mitm maison. Donc du coup avant tout et comme c'est mon premier message sur le forum, je tiens à remercier la communauté et ceux qui ont partagé différents ressources sur le sujet !

Donc le contexte : je développe un bot mitm, qui actuellement est capable de de voir les messages reçus et envoyés depuis le client, couplé par autoit pour automatiser des actions. Sauf que ce dernier me convient pas : par exemple sur mon module de bot chasse, je le laisse tourner sur un 2eme écran et en attendant je fais autre choses, comme développer ! Et bien l'autoit pour les clic sur une fenêtre sans avoir le focus de cette dernière c'est bien, mais dès qu'il s'agit de devoir taper du texte pour chercher un zaap ou lancer la commande /travel, sans le focus de la fenêtre il y a parfois des effets de bords. Puis j'ai aussi la problématique que quand je clique sur la map pour le déplacement, parfois j'aggro un montre sans le vouloir...

Donc du coup en analysant les paquets qui partaient, je me suis dit que ça serait plus simple d'envoyer mes propres paquets (surtout pour le déplacement).
En analysant les systèmes de sécurités, je comprends que certains messages envoyés au serveur sont suffixé de leur propre hash, où la fonction de hashage est envoyée par ce fameux paquet RawDataMessage. Analyser et interpréter ce message n'est pas un soucis, mais je me suis heurté à un problème.

Dans la méthode addCryptedHash (HumanCheck.as du RawDataMessage), une variable hashKey est initialisé à partir d'une valeur qui s'appelle le gameServerTicket, et cette dernière ce n'est rien d'autre que la valeur ticket décodée avec AES d'un précédent message SelectedServerDataMessage.
Pour décoder moi-même ce ticket en interceptant le message SelectedServerDataMessage, je me rends compte qu'il me faut une valeur appelée aesKey générée par le client de manière aléatoire.
Je vois que cette valeur est envoyée au serveur dans les credentials dans le IdentificationMessage, où credentials est chiffré en RSA, donc à priori pas possible de récupérer cette valeur ici.

Donc ma question : est-il possible réellement de récupérer cette valeur qui est l'aesKey ? Peut-être avec des pointeurs en mémoire (mais bon ça m'a l'air un peu complexe comme solution ^^).
Je me suis posé la question à un moment de "tricher" et utiliser un client dofus modifié, avec un script qui applique mon patch de modification à chaque mis à jour, tout en interceptant et falsifiant la réponse du CheckFileMessage histoire de faire passer ça inaperçu aux yeux des modos.

J'espère avoir été à peu près clair, et merci d'avance pour votre aide !
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
1 990
Reactions
112
#3
Exact,

Oubli autoit, honnêtement
 
Inscrit
31 Janvier 2021
Messages
5
Reactions
1
#5
Ok, j'ai fouillé un peu plus le forum, je trouve pas ou ne comprends pas quand certains parle de Mitm + RDM (qui est pourtant mon cas).
Donc en résumant :
1) Un tableau de 32 bytes est généré aléatoirement, qui correspond à une valeur AESkey (bien avant la réception du RDM, donc je pense que le RNG n'est pas modifié à ce moment là)
Code:
private var _AESKey:ByteArray;
Code:
      private function generateRandomAESKey() : ByteArray
      {
         var ba:ByteArray = new ByteArray();
         for(var i:int = 0; i < AES_KEY_LENGTH; i++)
         {
            ba[i] = Math.floor(Math.random() * 256);
         }
         return ba;
      }
Code:
      public function initAESKey() : void
      {
         this._AESKey = this.generateRandomAESKey();
      }
2) Cette valeur AESKey, est envoyée via le champ credential à un moment (avec user, mdp, etc....) dans le message IdentificationMessage, et ce champ credential est chiffré avant envoie en RSA
Code:
            case msg is HelloConnectMessage:
               hcmsg = HelloConnectMessage(msg);
               AuthentificationManager.getInstance().setPublicKey(hcmsg.key);
               AuthentificationManager.getInstance().setSalt(hcmsg.salt);
               AuthentificationManager.getInstance().initAESKey();
               iMsg = AuthentificationManager.getInstance().getIdentificationMessage();
Code:
      private function cipherRsa(login:String, pwd:String, certificate:TrustCertificate) : Vector.<int>
      {
         var baOut:ByteArray = null;
         var debugOutput:ByteArray = null;
         var n:int = 0;
         var baIn:ByteArray = new ByteArray();
         baIn.writeUTFBytes(this._salt);
         baIn.writeBytes(this._AESKey);
         if(certificate)
         {
            baIn.writeUnsignedInt(certificate.id);
            baIn.writeUTFBytes(certificate.hash);
         }
         baIn.writeByte(login.length);
         baIn.writeUTFBytes(login);
         baIn.writeUTFBytes(pwd);
         try
         {
            if(File.applicationDirectory.resolvePath("debug-login.txt") || File.applicationDirectory.resolvePath("debuglogin.txt"))
            {
               _log.debug("login with certificate");
               debugOutput = new ByteArray();
               baIn.position = 0;
               debugOutput.position = 0;
               debugOutput = RSA.publicEncrypt((new PUBLIC_KEY_V2() as ByteArray).readUTFBytes((new PUBLIC_KEY_V2() as ByteArray).length),baIn);
            }
         }
         catch(e:Error)
         {
            _log.error("Erreur lors du log des informations de login " + e.getStackTrace());
         }
         baOut = RSA.publicEncrypt(this._publicKey,baIn);
         var ret:Vector.<int> = new Vector.<int>();
         baOut.position = 0;
         var i:int = 0;
         while(baOut.bytesAvailable != 0)
         {
            n = baOut.readByte();
            ret[i] = n;
            i++;
         }
         return ret;
      }
3) La valeur _AESKey est utilisé pour décoder le ticket du message reçu : SelectedServerDataMessage, pour être ensuite stocké dans le champ gameServerTicket de AuthentificationManager
Code:
         ssdmsg = msg as SelectedServerDataMessage;
         ConnectionsHandler.connectionGonnaBeClosed(DisconnectionReasonEnum.SWITCHING_TO_GAME_SERVER);
         this._selectedServer = ssdmsg;
         AuthentificationManager.getInstance().gameServerTicket = AuthentificationManager.getInstance().decodeWithAES(ssdmsg.ticket).toString();
4) Dans la fonction addCryptedMessage de HumanCheck (reçu du RawDataMessage), un champ _hashKey est généré à partir d'un hash du champ gameServerTicket de AuthentificationManager


Donc, j'ai effectivement dit que je cherchais à récupérer la valeur _AESKey, car pour de ce que je sais pour l'instant, c'est le seul moyen d'avoir le gameServerTicket de AuthentificationManager (en interceptant SelectedServerDataMessage via un sniffer et de déchiffrer le ticket), qui est véritablement l'information que je cherche.
Une recherche plain text dans mon ide préféré, et je ne trouve rien me permettant d'intercepter, à un moment cette information.
Capture d’écran 2022-01-10 211704.png

Actuellement les pistes que j'envisage :
* Utiliser un dofusInvoker.swf patché au lancement de dofus, puis le remplacer immadiatement par celui d'origine, histoire de ne pas à avoir à gérer les messages modo CheckFileMessage, mais en revanche possible détection dans le future que le swf chargé en mémoire par le processus dofus.exe n'est pas le bon (c'est pas impossible on dirait, ffdec permet de le faire). Ce patch par exemple pourrait intégrer un nouveau message du protocole dofus (pas de nouvelle classe, juste un parsing à la main) et qui prend en entrée un ByteArray et qui retourne ce ByteArray avec son hash ajouté à la fin (j'appelle le HASH_FUNCTION en gros). Comme ça mon bot fabrique son paquet custom, l'envoi au client dofus pour hash, puis récupère le résultat et le renvoi au serveur.

* Enfin de compte j'en ai pas d'autre (à part retourner sur du clic)
 
Haut Bas