Procédure d'authentification et RSA

Arth

Contributeur
Inscrit
28 Aout 2016
Messages
80
Reactions
3
#1
Salut à tous,

Je développe en ce moment un petit bot Do fus avec un collègue, le but étant principalement de bosser sur une IA. (le tout en C++ avec la lib Boost et STL)
Pour le moment il ne s’agit de rien de très sérieux car il n'est pas certain que nous puissions passer beaucoup de temps sur ce projet, voyons ça plus comme un passe temps ^^.
Mais à terme, si le projet devenait sérieux, il pourrait être intéressant de rendre le projet open-source.

En cherchant un peu je suis tombé sur différent sujet très instructif sur ce forum, du coup comme je bloque un peu en ce moment je me dit qu'il y à sûrement ici des gens qui peuvent m'informer un peu.

Le problème est encore une question sur le packet HelloConnect, j'ai bien lu au moins 3 ou 4 sujets sur ce packet ici mais pourtant je constate des choses pas claire du tout.

Dans les anciennes versions du client on trouvait une constante PUBLIC_KEY, qui servait (d’après ce que j'ai pu lire ici) à décoder une private key avec laquelle le client devait chiffrer le password (une fois salté puis hashé).

Il y a déjà 2 choses qui me dérange :
  • (1) Je ne connaît que très peu RSA, mais il me semble qu'une public key sert à chiffrer et non à déchiffrer et inversement pour la private key. S'agit-t-il d'une confusion au cœur même des sources du client ou alors j'ai raté un truc ?
  • (2) Le fait que le password soit salt puis hash semble indiquer que les password des clients sont enregistré en claire dans la DB du serveur officiel o_O ? (ou alors j'ai raté un double hash ?)

Du coup j'ai considéré que la clef static que l'on trouve sur d'autre sujet est une clef RSA privé et non public.
Dans un premier temps, avant de commencer à coder un truc pour décoder la key, j'essai simplement de le faire avec openssl dans un terminal:
openssl rsautl -verify -inkey static-private-key.pem -in public-key.pem.enc -out public-key.pem

mais je n'obtient que des "unable to load Private Key" ...
Et lorsque que je vérifie avec openssl la key (openssl rsa -in static-private-key.pem -check) et de la même manière il me répond que cette key n'est pas une clef privée.
J'ai aussi tester de le considérer comme un certificat mais rien ne semble passé (je precise que j'ai bien changé les header footer à chaque fois) du coup je me demande :
  • (3) J'ai tord sur mon raisonnement de public/private key ou il s'agit d'un certificat ? (ou alors je ne sais simplement pas utiliser openssl :p et c'est fort probable)
  • (4) La key a-t-elle changé depuis le temps ?

Et une dernière question, (5) où ce trouve maintenant, dans les sources, la public key (enfin verifykey) ? la class qui devrait la contenir est vide, c'est un système d'anti-retro-engineering ?

Merci d'avance !

EDIT:
Le fait d’écrire ce post m'a finalement débloqué sur quelque unes de mes questions:
(5) J'ai finalement trouvé où ce trouvé la nouvelle key, est ce trouve dans les binaryData.
(4) Et elle a bien changée depuis le temps.
(3) Et effectivement il faut bien une PublicKey dans pour faire un verify (ce qui n'est visiblement pas exactement un decryptage) intéressant comme outil =). Mais je n'arrive toujours pas à decoder avec openssl :
openssl rsautl -verify -inkey static-private-key.pem -keyform PEM -pubin -in public-key.pem.enc.bin
j'obtiens l'erreur : "RSA operation error
140504773994128:error:0406706C:rsa routines:RSA_EAY_PUBLIC_DECRYPT:data greater than mod len:rsa_eay.c:661"

J'imagine que c'est le choix de l'algo qui passe pas.
 
Dernière édition:

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#2
Bonjour et bienvenue @Arth,

Dans les anciennes versions du client on trouvait une constante PUBLIC_KEY, qui servait (d’après ce que j'ai pu lire ici) à décoder une private key avec laquelle le client devait chiffrer le password (une fois salté puis hashé).
Alors il y a eu deux grosses étapes dans l'histoire du chiffrement dofus, la première version était en MD5, puis la seconde en RSA qui n'a jamais été réellement modifiée depuis.
Tu as donc dans les données binaires du jeu une publicKey, qui te permet de chiffrer tes identifiants, puis de l'autre côté, le serveur pourra les décrypter à l'aide d'une privateKey qui dépend de cette même publicKey.

https://fr.wikipedia.org/wiki/Chiffrement_RSA#Fonctionnement_g.C3.A9n.C3.A9ral
Le chiffrement RSA est asymétrique : il utilise une paire de clés (des nombres entiers) composée d'une clé publique pour chiffrer et d'une clé privée pour déchiffrer des données confidentielles. Les deux clés sont créées par une personne, souvent nommée par convention Alice, qui souhaite que lui soient envoyées des données confidentielles. Alice rend la clé publique accessible. Cette clé est utilisée par ses correspondants (Bob, etc.) pour chiffrer les données qui lui sont envoyées. La clé privée est quant à elle réservée à Alice, et lui permet de déchiffrer ces données. La clé privée peut aussi être utilisée par Alice pour signer une donnée qu'elle envoie, la clé publique permettant à n'importe lequel de ses correspondants de vérifier la signature.
  • Le fait que le password soit salt puis hash semble indiquer que les password des clients sont enregistré en claire dans la DB du serveur officiel o_O ? (ou alors j'ai raté un double hash ?)
Le password n'est ni salt ni hash, le salt est écrit à la suite de la crypto, mais ne fait en aucun cas partie des identifiants en lui même.

Code:
private function cipherRsa(param1:String, param2:String, param3:TrustCertificate) : Vector.<int>
  {
  var baOut:ByteArray = null;
  var debugOutput:ByteArray = null;
  var n:int = 0;
  var login:String = param1;
  var pwd:String = param2;
  var certificate:TrustCertificate = param3;
  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);
Tu devrais vérifier la classe AuthentificationManager.
 

Arth

Contributeur
Inscrit
28 Aout 2016
Messages
80
Reactions
3
#3
D'accord, merci pour tes précisions @BlueDream ^^.
Pour le chiffrage du login, c'est vrai que j'ai pas encore fait beaucoup de recherche puisque je restais bloquer sur le verify. mais je vais enchaîner sur ça, donc ton message va me servir je pense.

J'ai finalement réussi à décoder cette foutu publicKey manuellement, en faite je suis trop bête, j'ai oublier de retirer le message Pong qui est envoyer dans le même packet de mon analyse manuelle (c'est trop bête car mon code le fait déjà, j'ai donc perdu du temps pour rien, mais bon ^^).
Je donne donc la démarche pour verifier la publicKey manuellement, des fois que quelqu'un cherche à le faire ... on sais jamais. Je suis sous linux alors j'utilise les commandes unix et openssl :

  1. Extraire la clef public static obtenu par décompilation des sources (je nomme le fichier static-public-key.pem)
  2. Extraire la publicKey signé du packet (sans oublier de retirer le message pong a la fin du packet ^^), via Wireshark par exemple. Les données sont en hex, je les stocks dans le fichier publickey.sign.hex
  3. Convertir le fichier publickey.hex en fichier binaire :
    Code:
    cat publikey.sign.hex | xxd -r -p > publickey.sign.bin
  4. Vérifier les données signé :
    Code:
    openssl rsautl -verify -in publickey.sign.bin -inkey static-public-key.pem -pubin -keyform PEM -out publickey.pem.bin
  5. Convertir publickey.pem.bin en base64 :
    Code:
    base64 publickey.pem.bin > publickey.pem
  6. Ajouter les HEADER et FOOTER et puis hop.

Du coup je vais attaquer le code, et si quelqu'un connaît une bonne lib C++ pour RSA basé sur STL/Boost je suis tout ouïe. Sinon je vais me débrouiller :p.
 
Dernière édition par un modérateur:
Inscrit
5 Octobre 2016
Messages
4
Reactions
0
#4
Yo tout le monde !

Je suis nouveau sur le forum et dans la programmation de bot en socket aussi. Ces précisions sur la procédure d'authentification vont m'être très utile je pense !!

@BlueDream qu'utilise tu pour dé-compiler les fichiers .SWF ? Moi j'utilise JPEXS et j'obtient un résultat totalement différent, et plutôt incompréhensible, pour la fonction cipherRsa() (et quelques autres fonctions aussi d'ailleurs).

Voici un extrait :

Code:
private function cipherRsa(param1:String, param2:String, param3:TrustCertificate) : Vector.<int>
      {
         var baOut:ByteArray = null;
         if(!_loc7_)
         {
         }
         var debugOutput:ByteArray = null;
         if(!_loc6_)
         {
            loop0:
            while(true)
            {
               var certificate:TrustCertificate = param3;
               if(!_loc6_)
               {
                  addr43:
                  while(true)
                  {
                     baIn.writeUTFBytes(this._salt);
                     if(_loc7_)
                     {
                        addr54:
                        while(true)
                        {
                           var login:String = param1;
                           if(!_loc7_)
                           {
                           }
                           var pwd:String = param2;
                           if(!_loc7_)
                           {
                              break;
                           }
                        }
                        continue loop0;
                     }
                     break;
                  }
...
Et la fonction getHash() de ShiledCertificate.as ...

Code:
private function getHash(param1:Boolean = false) : String
      {
         var virtualNetworkRegExpr:RegExp = null;
         if(_loc6_)
         {
            continue loop0;
         }
         continue loop4;
      }
 
Dernière édition par un modérateur:
Inscrit
5 Octobre 2016
Messages
4
Reactions
0
#6
J'ai activer la déobfuscation auto sur la version 9.0 et sa marche impeccable ! Merci !! :)

Je testerais la version 6.0.2 pour voir d'éventuelles différences.
 
Inscrit
5 Octobre 2016
Messages
4
Reactions
0
#7
Du coup j'ai continué mes recherches et j'ai encore quelques questions .. ^^ Mon objectif étant de coder un bot socket capable de s'authentifier au serveur de login.

1) A quoi sert la clé envoyé par le serveur via le paquet HelloConnectMessage ?

2) Dans tout ce qui est chiffré par cipherRsa(), à quoi sert _AESKey ? Si je remplace ça par n'importe quelle chaîne de 32 octets sa fait l'affaire non !?
Code:
private function cipherRsa(param1:String, param2:String, param3:TrustCertificate) : Vector.<int>
      {
         var baOut:ByteArray = null;
         var debugOutput:ByteArray = null;
         var n:int = 0;
         var login:String = param1;
         var pwd:String = param2;
         var certificate:TrustCertificate = param3;
         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);
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#8
Il s'agit de la PublicKey, qui une fois réceptionnée par le jeu, est décodée via une autre key contenue dans le client.

https://fr.wikipedia.org/wiki/Chiffrement_RSA#Fonctionnement_g.C3.A9n.C3.A9ral
Le chiffrement RSA est asymétrique : il utilise une paire de clés (des nombres entiers) composée d'une clé publique pour chiffrer et d'une clé privée pour déchiffrer des données confidentielles. Les deux clés sont créées par une personne, souvent nommée par convention Alice, qui souhaite que lui soient envoyées des données confidentielles. Alice rend la clé publique accessible. Cette clé est utilisée par ses correspondants (Bob, etc.) pour chiffrer les données qui lui sont envoyées. La clé privée est quant à elle réservée à Alice, et lui permet de déchiffrer ces données. La clé privée peut aussi être utilisée par Alice pour signer une donnée qu'elle envoie, la clé publique permettant à n'importe lequel de ses correspondants de vérifier la signature.
De toute façon tout le fonctionnement est dans les sources, tu as juste à étudier cela.

Pour ce qui est de l'AESKey, tu dois la générer par toi même, comme indiqué dans les sources:

Code:
private function generateRandomAESKey() : ByteArray
      {
         var _loc1_:ByteArray = new ByteArray();
         while(0 < AES_KEY_LENGTH)
         {
            _loc1_[0] = Math.floor(Math.random() * 256);
            _loc2_++;
         }
         return _loc1_;
      }
Elle te servira par la suite à décrypter le gameServerTicket plus tard dans la connexion lors du switch vers le serveur de jeu.
 
Inscrit
5 Octobre 2016
Messages
4
Reactions
0
#9
Ok très bien, je regardais ça de plus près.

Thanks :)
 
Haut Bas