Details par catégorie : Code

Titre Date de publication
Details Décrypter les cookies Commerce Server 2002

Comment décrypter le contenu des cookies MSCSAuth Commerce Server 2002 sans l'avoir installé.

Voici ce que contiennent ces cookies : 983XXXXCC620F5E6D8FDEXXX29663AF16BE900XXXC8589115C7DE7XXX8570FF8C04B2B2EXXXX428DCB0F92A2292.....

C'est bien sûr encrypté avec la clef que l'on peut trouver dans la configuration : Encryption Key for Cookie Data

configuration cs


Ce qu'il faut savoir c'est qu'un filtre ISAPI fait le travail d'authentification c'est mscsauth.dll qui est placé dans les filtres au niveau de IIS.

Donc on va chercher dans cette dll comment ils font :)

Pour cela on démarre avec une piste, on sait que GetUserIdFromCookie(...) va forcement faire le travaille de décryptage avant de nous répondre.

Décompilons (j'ai utilisé PE Explorer en version d'évaluation)

Cliquez sur les images pour y voir clair.

mini cs1

Nous voici arrivés sur la fonction GetUserIdFromCookie(...), cherchons ce qu'il s'y passe d'intéressant.

Plus bas on découvre :

mini cs2

Un appel à DecryptData(...), c'est exactement ce que l'on cherche.

mini cs3

A l'intérieur de DecryptData(...), qu'est ce qu'on découvre ?

Un appel à CryptAcquireContextW(...) de la dll ADVAPI32.dll, c'est la DLL de base des fonctions de crypto.

A l'adresse 0x1000FC65 on colle un pointeur sur la fonction dans le registre edi, que l'on appel ensuite à l'adresse 0x1000FC7A, entre ces deux adresses on empile les arguments.

le premier argument empilé, donc le dernier de la fonction c'est

  • CRYPT_VERIFYCONTEXT = 0xF0000000
  • ensuite c'est le type de provider PROV_RSA_FULL = 1, suivi du nom du provider MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"
  • Les arguments suivants sont moins évidents, il y a un push eax à l'adresse 0x1000FC78, plus haut on voit que eax est mis à 0x0 via la ligne xor eax,eax, adresse 0x1000FC53.
  • Le dernier est notre pointeur sur le provider qui sera initialisé.


Avec tout ça on a juste un CryptoServiceProvider, voyons la suite.

mini cs4

Ici on continue dans la même fonction et on trouve un appel à la fonction HrImportSessionKey, allons voir :



Ce listing n'a pas vraiment d'intérêt sauf le petit jump à l'adresse : 0x1000FEC9

mini cs8

Ce jump nous mène à une routine qui fait appel a la fonction New_GenerateSessionKeyFromPassword, c'est dans cette fonction que va se passer tout le gros du travail.

mini cs9

On y retrouve toutes les fonctions intéressantes de la cryptoAPI : CryptCreateHash, CryptHashData, CryptDeriveKey.

L'appel à CryptCreateHash prends en paramètre (comme d'habitude les derniers sont les premiers) : 

  • push eax, un pointeur qui nous sera rempli avec le hash crée
  • ensuite push edi qui est mis a 0x0 via un xor que je vous laisse trouver vous même, c'est un flag non utilisé (uint dwFlags)
  • encore push edi cette fois ci c'est notre paramètre hKey, qui n'est pas utilisé dans notre cas
  • ensuite push 00008004h, qui corresponds à un algo de cryptage (différents algo de cryptage)
  • pour finir notre pointeur sur le provider que l'on a déjà initialisé plus haut.


L'appel suivant CryptHashData

  • push edi qui vaut toujours 0x0
  • push 00000010h qui est notre longueur en byte de la clef (uint dwDataLen)
  • push eax qui est le tableau de byte de notre clef (celle que l'on a trouvé dans la configuration)
  • push [ebp-04h] qui est le hash crée précédement


L'appel suivant CryptDeriveKey

  • push [ebp+08h] est notre pointeur sur la clef qui sera générée par l'appel
  • push edi qui encore 0x0
  • push [ebp-04h] qui est notre hash
  • push 00006602h encore un algo de cryptage (différents algo de cryptage)
  • le dernier push est notre provider

Avec tout ce travail on ne va garder que la clef donc notre [ebp+08h], le reste c'est poubelle c'est pour ça qu'on voit un appel a CryptDestroyHash que je ne vais pas expliquer.
On remonte là d'oû on est parti, c'est a dire au moment ou l'on trouve l'appel a HrImportSessionKey, un peu plus bas on découvre des appels à CryptDecrypt, enfin.

mini cs5



Les appel a CryptDecrypt prennent en paramètres :
  • la longueur des données cryptées à traiter, cette valeur sera écrasée avec la longueur des données décryptés.
  • les fameuses données chiffrées que l'on a recuperées dans le cookie MSCSAuth, les données décryptées seront placées ici puisque leur longueur est forcement plus courte que la version cryptées ça rentre.
  • une valeur de dwFlags qui est encore 00000000h
  • un flag permettant de dire si c'est le dernier block de données à décrypter on met 1 puisque l'on passe le cookie au complet (00000001h).
  • le pointeur sur le hash pour les algo qui l'utilisent, nous non donc 00000000h
  • le pointeur sur la clef dérivée plus haut

Vous trouverez ici le code c# qui permet de le faire en utilisant les pInvoke sur ADVAPI32.dll

Si vous avec l'équivalent en pur c# sans pInvoke je suis preneur.

2/6/2010 12:43 PM
Details EventHandler 2 le retour

Afin de conclure sur le sujet des EventHandler

Voici la version formatée et testée du code de Vincent voir le commentaire du post précédent.

EventHandler handler = queryControl1.CompletedHandler; 

foreach (EventInfo eventInfo in MdfAPIClient.GetType().GetEvents())

      {

Delegate del =

Delegate.CreateDelegate(eventInfo.EventHandlerType, queryControl1, handler.Method); 

      eventInfo.AddEventHandler(MdfAPIClient, del);

      }

Evidemment c’est beaucoup plus lisible et c’est bien sur la meilleure façon de faire :)

Cela peux même tenir en une ligne si on veux :

MdfAPIClient.GetType().GetEvents().ToList().ForEach(evnt => evnt.AddEventHandler(MdfAPIClient, Delegate.CreateDelegate(evnt.EventHandlerType, queryControl1, ((EventHandler)queryControl1.CompletedHandler).Method)));

1/18/2010 9:07 AM
Details EventHandler générique

Récemment pendant le développement d’un client de test pour un web service, je me suis retrouvé à devoir appeler une même fonction pour tout les évènements *Completed du proxy, le problème est que les EventHandlers du proxy ont tous un prototype différent :

public delegate void PrepareSearchCompletedEventHandler(object sender, PrepareSearchCompletedEventArgs e);

Dans notre cas c’est le 2ème argument qui est typé sur chaque EventHandler.

Quel est l’utilisation pratique d’une telle bidouille ?

Cela me permet simplement de mettre à jour un control avec le contenu SOAP de la réponse du service, un peu comme une fenêtre de log.

Voici la fonction que je souhaite appeler pour chacun de ces évènements :

public void CompletedHandler(object sender, object e);

Et voila comment j’ai fait :



// Pour chaque events

foreach (EventInfo eventInfo in MdfAPIClient.GetType().GetEvents())

{

// Je prends une liste des paramètre du handler

List<Type> param =

(from t in eventInfo.EventHandlerType.GetMethod("Invoke").GetParameters() select t.ParameterType).

ToList();

// J’y ajoute le type qui déclare la fonction que je souhaite appeler

// c’est avec cet argument que l’on passera notre instance

param.Insert(0, typeof(QueryControl));

// Je déclare ma DynamicMethod avec

// le 1er argument est le nom de la méthode pas utile

// le 2nd est le type de retour de cette methode

// le 3ème est le tableau de type d’arguments

// le 4ème le type a qui la méthode sera appropriée

System.Reflection.Emit.DynamicMethod handler = new

DynamicMethod("", typeof(void), param.ToArray(), typeof(QueryControl));

// On recupere le générateur IL

ILGenerator ilGenerator = handler.GetILGenerator();

// Je récupere la méthode que je souhaite appeler ici c’est CompletedHandler du type QueryControl

MethodInfo genHandler = typeof(QueryControl).GetMethod("CompletedHandler");

// Début du contenu de l’event dynamique

// Je re-empile les arguments avec lesquels on m’a appelé (l’instance, le 1er arg (sender), le 2nd arg un type dérivé de EventArgs (e)

ilGenerator.Emit(OpCodes.Ldarg_0);

ilGenerator.Emit(OpCodes.Ldarg_1);

ilGenerator.Emit(OpCodes.Ldarg_2);

// On appel la fonction souhaité

ilGenerator.Emit(OpCodes.Call, genHandler);

// Puis on Retourne

ilGenerator.Emit(OpCodes.Ret);

// Pour finir on ajoute notre eventhandler a notre proxy : MdfAPIClient

eventInfo.AddEventHandler(MdfAPIClient, handler.CreateDelegate(eventInfo.EventHandlerType, queryControl1));

}



Tout ça pour éviter d’écrire :


{

MonProxyWS proxy = new MonProxyWS();


// Puis pour chaque EventHandlers XXX

proxy.XXXCompleted += new XXXCompletedEventHandler(proxy_XXXCompleted);

}


// Ainsi que mon event target différent aussi pour chaque XXX

void proxy_XXXCompleted(object sender, XXXCompletedEventArgs e)

{

queryControl1.CompletedHandler(sender, e);

}




Soit beaucoup de code redondant et sans valeur ajoutée.

Il y a peu être une méthode plus simple ou une implémentation plus claire/propre, dans ce cas laissez un commentaire

 

Edit :

 

Une autre méthode aurait été de faire un Tracelistener qui débite dans le controle voulu, et d'activer les traces network comme ici. Bien sur il existe un tas de trace sources et switches à tous les niveaux, faîtes votre choix.

1/12/2010 1:29 PM