
Shellcode Windows x64 en mode noyau, fabriqué à la main, pour le vol de jetons (token stealing)
Windows x64 shellcode en mode noyau conçu à la main pour remplacer le jeton d'accès principal du processus en cours d'exécution par le jeton du processus SYSTEM pour une élévation de privilèges (Elevation of Privilege(EoP)).
Windows 7/Windows Server 2008 R2 Build 7601Windows 8/Windows Server 2012 Build 9200Windows 8.1/Windows Server 2012 R2 Build 9600Windows 10 1507/TS1 Build 10240Windows 10 1511/TS2 Build 10586Windows 10 1607/RS1/Windows Server 2016 Build 14393Windows 10 1703/RS2 Build 15063Windows 10 1709/RS3 Build 16299Windows 10 1803/RS4 Build 17134Windows 10 1809/RS5/Windows Server 2019 Build 17763Windows 10 1903/19H1 Build 18362Windows 10 1909/19H2 Build 18363Windows 10 2004/20H1 Build 19041Windows 10 2009/20H2 Build 19042Windows 10 2104/21H1 Build 19043Windows 10 2110/21H2 Build 19044Les prérequis pour compiler ce projet sont :
Visual Studio 2019(any edition will do fine)Windows 10 SDK, version 2004Windows 10 WDK, version 2004Python3Il faut noter ici qu'un simple assembleur suffit (ce projet utilise MASM), car techniquement, c'est tout ce dont vous avez besoin.
Après avoir installé ce qui précède, il suffit d'ouvrir la solution avec Visual Studio et de compiler pour la cible x64.
Après une compilation réussie, les binaires se trouvent dans le répertoire Bin, sous le sous-répertoire correspondant à l'architecture.
Vous pouvez également télécharger un shellcode indépendant de la position, prêt à déployer, depuis Releases.
Veuillez NE PAS essayer de déployer la charge utile sur une machine dont vous dépendez pour travailler si vous n'êtes pas sûr de son fonctionnement.
Reportez-vous à la documentation Microsoft pour toute information supplémentaire.
À des fins de test, je recommande vivement d'utiliser flare-kscldr pour déployer le shellcode en mode noyau sur une VM de test, et le guide de configuration système de CodeMachine pour le développement et le débogage du noyau pour configurer une VM invitée Hyper-V avec une prise en charge complète du débogage du noyau.
Vous pouvez également envisager d'automatiser le processus avec kdbg-driver-vagrant pour déployer rapidement une VM de test avec un débogage complet du noyau via Vagrant.

Comme me l'a fait remarquer Dmytro Oleksiuk(@d_olex), il existe dans le code des conditions de concurrence (race conditions) plutôt flagrantes, liées spécifiquement à :
nt!_EPROCESS reliées entre elles par une liste doublement chaînée circulaire, sans utiliser de primitive de synchronisation/mécanisme de verrouillageIls ne disposent actuellement d'aucune protection contre les modifications qui leur sont apportées pendant que nous les manipulons.
Est-ce un problème ? Oui, les conditions de concurrence sont toujours problématiques et peuvent provoquer toutes sortes de comportements indéfinis et de désagréments sous forme de bugchecks.
L'utilisation de cette charge utile affectera-t-elle la stabilité de mon exploit ? Peut-être.
Alors, quelle est la solution ? La solution comporte deux étapes.
La partie 1 consiste à acquérir un verrou de type attente comme les pushlocks - nt!PspActiveProcessLock(pointeur de pushlock) pour un accès exclusif via nt!ExAcquirePushLockExclusive, avant de parcourir la liste des processus (la livraison normale des APC du noyau doit être désactivée au préalable), puis nt!ExReleasePushLockExclusive pour libérer le verrou une fois que nous avons fini d'utiliser la liste, moment auquel la livraison normale des APC du noyau doit être réactivée.
Cependant, étant donné que cette variable globale n'est pas exportée par le noyau nt, une approche bien plus propre et plus sûre consisterait à utiliser l'API nt!ZwQuerySystemInformation avec SYSTEM_INFORMATION_CLASS == SystemProcessInformation pour trouver le PID à partir de l'ImageName, puis nt!PsLookupProcessByProcessId pour obtenir nt!_EPROCESS VA à partir du PID.
Si vous êtes curieux de savoir comment le noyau procède pour la première approche, je vous invite à examiner nt!PsGetNextProcess dans un désassembleur.
La partie 2 consiste à référencer en toute sécurité les objets à l'aide de la famille d'APIs nt!ObReferenceObject afin d'augmenter le compteur de références de l'objet processus pour qu'il ne puisse pas être supprimé tant que nous ne l'avons pas explicitement décrémenté à la fin, une fois que nous en avons terminé, à l'aide de nt!ObDereferenceObject.
Notez qu'augmenter manuellement le compteur de références est redondant, car un appel à nt!PsLookupProcessByProcessId, s'il aboutit, le fait pour nous.
La mise en œuvre de ces correctifs nécessiterait toutefois de trouver l'adresse de base de ntoskrnl.exe et d'y résoudre les symboles en parcourant l'EAT pour localiser les pointeurs de fonction à l'aide d'un algorithme de hachage de chaînes, ce qui augmenterait considérablement la taille de la charge utile.
Je déciderai peut-être de l'implémenter un jour, ou simplement d'écrire en C et de spammer la sortie du compilateur :)
Merci à Dmytro Oleksiuk(@d_olex) et à Paul L.(@am0nsec) d'avoir signalé la ou les erreurs et d'avoir également suggéré le correctif.