Windows et les privilèges – Usurpation d’un Access Token

Avant d’entamer une série d’articles sur les privilèges sous Windows, on va commencer par une démo, un classique vieux de plus de 10 ans mais qui fait toujours son effet.

La démo consiste à lancer une invite de commandes en tant que simple utilisateur puis à remplacer en mémoire son Access Token (il contient entre autre la liste de ses privilèges) par celui d’un processus à haut privilèges.

Cette technique est encore largement utilisée pour de l’élévation de privilèges notamment au travers de drivers vulnérables à de l’écriture arbitraire.

La démo repose sur 2 Virtual Machines, l’une étant un Windows avec un debugger Windbg en mode Kernel et l’autre étant un Windows où tourne un invite de commandes (cmd.exe).

Je ne vais pas détailler la mise en oeuvre de cette plateforme de démo car elle est facile à trouver sur le Net.

Contexte mémoire : les 2 VMs sont démarrées et on a lancé un cmd.exe sur la VM debuggee en mode simple utilisateur. Comme attendu, on ne dispose que des privilèges standard :

Côté VM debugger, sous Windbg, on commence par lister tous les processus avec la commande !dml_proc. On y trouve entre autre le processus System à haut privilèges et le processus à faible privilèges cmd.exe.

On voit que le processus System est à l’adresse mémoire 842be020 mais ce qui nous intéresse est l’adresse mémoire de son Token.

Pour y accéder, il faut d’abord connaître la structure mémoire d’un processus avec la commande dt nt!_EPROCESS :

On voit que l’adresse mémoire du Token est à l’offset 0x0f8.
Cette adresse est un pointeur un peu particulier car les 3 derniers bits ne sont pas utilisés pour le calcul de l’adresse, comme le montre la commande dt nt!_EX_FAST_REF (il s’agit de la structure d’un union en C) :

La vraie adresse du Token est en fait la valeur affichée par « Value » à laquelle on masque les 3 derniers bits (certaines structures de données Kernel ont un alignement particulier et Windows utilise les bits non utilisés par ces pointeurs…).

On commence donc par récupérer la valeur à l’offset 0x0f8 du processus System, soit 0x890014a7 :

Comme on l’a vu il faut masquer les 3 derniers bits ce qui donne 0x890014a0.

Cette adresse 0x890014a0 est l’adresse mémoire du Token du processus System. C’est cette adresse qu’on mettra dans l’espace mémoire du processus cible cmd.exe au niveau de son Token.

Maintenant intéressons-nous justement au processus cible, cmd.exe.

On récupère son RefCnt, c’est en fait la valeur des 3 derniers bits du pointeur Token, ici on obtient 001:

Il faut maintenant fusionner la valeur 0x890014a0 avec le RefCnt du processus cmd.exe, ce qui donne 0x890014a1 (on a simplement fait un OR).

Maintenant il n’y a plus qu’à remplacer le pointeur du Token dans le processus cible cmd.exe avec la command ed <adresse> <valeur>.

L’adresse qu’on veut modifier est l’adresse mémoire du processus cmd.exe, 0x85bb4450 à laquelle on ajoute l’offset 0xf8 pour atteindre le Token.

On enlève le break, en continuant (g), et on va sur la VM debugger. Là on retourne sur la fenêtre de l’invite de commandes qui n’oublions pas a été lancée en tant que simple utilisateur.

Et voila, on est bien Local System avec un max de privilèges.