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

On a vu dans le précédent post qu’il est possible d’élever ses privilèges et de devenir administrateur local en copiant depuis l’espace Kernel le pointeur du Token System vers le pointeur du Token d’un invite de commandes. Alors oui c’était facile car nous étions en mode debug Kernel et tout se faisait à la main en Windbg, néanmoins cela a posé certaines bases. Nous verrons dans un prochain post comment exploiter un driver vulnérable à de l’écriture arbitraire pour élever ses privilèges selon une technique assez proche.

En attendant attardons-nous un peu sur ces Token, plus précisément les Windows Access Token, que sont-ils ?

Un Windows Access Token est un objet qui décrit le contexte de sécurité d’un processus ou d’une thread. Il est généré par le système lors du logon. Il est utilisé lorsqu’un processus ou une thread interagit avec un objet qui dispose d’un descripteur de sécurité, un « securable object ».

Un Windows Access Token contient pas mal d’attributs, dont :

  • le SID du propriétaire
  • les SID des groupes dont il est membre
  • la liste de ses privilèges
  • le type de Token (Primary ou Impersonation)

Une fois que le Token est créé il n’est pas possible d’ajouter de nouveaux privilèges, il est seulement possible d’activer ou de désactiver les privilèges déjà existant.

Mais qu’est-ce qu’un privilège ?

Un privilège est un droit attribué à un utilisateur ou un à un groupe afin d’effectuer diverses opérations sur le système comme éteindre la machine, chargé des drivers, ou changer l’heure.

Certains utilisateurs et groupes ont des privilèges prédéfinis comme les administrateurs locaux, les membres du groupe Backup ou Printer Operators.

Les privilèges sont aussi administrables à travers les stratégies locales (gpedit.msc) dans la section « Attribution des droits utilisateurs ».

La commande whoami /priv liste vos privilèges.

Pour observer la structure mémoire d’un Acces Token et des privilèges, retournons dans Windbg.

Examinons un processus cmd.exe avec la commande !process. On voit que son Access Token est référencé à l’adresse 972c0a28 :

Affichons maintenant le contenu du Token avec la commande !token. On trouve le SID de l’utilisateur ainsi que les SID des groupes dont il est membre. Enfin plus bas on trouve bien la liste des privilèges, ceux que l’on retrouve avec la commande whoami /priv

Si on regarde en mémoire, les privilèges sont représentés par une valeur « Present » où chaque bit correspond à un privilège. Noté également la valeur « Enabled » qui est la liste des privilèges activés (bit à 1) ou pas (bit à 0).

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.