
Implémentation d'un Beacon Object File pour le contournement de l'UAC par désérialisation d'Event Viewer.
Ceci est une implémentation Beacon Object File du contournement UAC par désérialisation de l'Observateur d'événements découvert par @orange_8361 et du POC réalisé par CsEnox.

Testé sur Win10/Win11 x64
Ce contournement UAC effectue les actions suivantes qui doivent être prises en compte en référence à l'OPSEC :
-1. Écrit un fichier binaire dans %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews
-2. Appelle ShellExecute() pour démarrer mmc.exe / Observateur d'événements
--A. L'Observateur d'événements s'ouvrira avec une interface graphique !
--B. L'Observateur d'événements appellera cmd.exe
---a. Cmd.exe appellera Taskkill afin de fermer mmc.exe
---b. Cmd.exe affichera un message d'erreur dans la fenêtre console parfois maintenue ouverte, dans le but de faire croire à l'utilisateur que tout va bien
---c. Cmd.exe exécutera toutes les autres commandes fournies par l'utilisateur
-3. Le fichier binaire est supprimé de %LOCALAPPDATA%\Microsoft\Event Viewer\RecentViews
Suivant la démonstration originale et le POC de CsEnox, ysoserial a été utilisé pour créer une charge utile initiale :

ysoserial.exe -o raw -f BinaryFormatter -g TextFormattingRunProperties -c "taskkill /f /im mmc.exe >nul && echo Windows has Recovered from an Unexpected Error. You may Close this Window. &&" > cmd.bin
Ce contournement UAC fonctionne en invoquant mmc.exe, qui s'ouvre à l'écran et doit être fermé. Malheureusement, je n'ai pas trouvé de moyen de démarrer mmc.exe sans que l'interface graphique ne s'ouvre ; j'ai examiné CreateProcess() et le drapeau CREATE_NO_WINDOW, cependant CreateProcess() ne fonctionne pas bien avec UAC. Les posts StackOverflow sur ce sujet m'ont orienté vers l'API ShellExecuteA() qui possède le drapeau SW_HIDE (et d'autres), mais cela n'a pas été fructueux. Le fait que mmc.exe s'affiche à l'écran semble inévitable.
Ysoserial appelle cmd.exe /c, donc cela est utilisé pour appeler taskkill afin de fermer mmc.exe. && est utilisé pour chaîner les commandes afin que l'action souhaitée à exécuter en tant qu'Administrateur puisse être effectuée. J'ai constaté lors des tests que dans certains cas, cette action maintenait ouverte l'invite cmd.exe générée par mmc.exe ; c'est le cas lors de l'appel de charges utiles d'injection locale (normales), comme la charge utile exécutable par défaut de Cobalt Strike. Pour y remédier, j'ai redirigé la sortie de la commande taskkill (qui s'affichait auparavant dans la fenêtre cmd.exe maintenue ouverte) vers /dev/null, et j'ai affiché à l'écran un message concernant une erreur qui a été traitée afin d'essayer de faire en sorte que ce comportement suspect n'alerte pas un utilisateur non averti.
Les charges utiles qui créent un processus, s'y injectent, puis se terminent ne maintiendront pas la fenêtre cmd.exe ouverte, et donc le message est inutile. De même, les commandes qui s'exécutent et se terminent (par exemple net user Administrator newpassword) ne maintiendront pas la fenêtre ouverte.
Vous pouvez exécuter des commandes PowerShell encodées via ce BOF :



La commande taskkill et le message à l'utilisateur sont intégrés dans la commande ysoserial. Si vous souhaitiez modifier ce comportement, vous devriez exécuter votre propre commande ysoserial et suivre la démonstration ci-dessous de la façon dont le code a été créé.
À l'aide d'un outil personnalisé, le fichier binaire a été ingéré et imprimé en hexadécimal :

En le copiant dans le Bloc-notes et en recherchant '&' en hexadécimal, on localise les octets souhaités (notez qu'il y a plusieurs ensembles de ceux-ci, nous avons besoin du dernier) :

La charge utile est divisée en deux parties immédiatement après ces octets :

La longueur de chaque partie est calculée et notée pour plus tard.
C'est là que les choses se compliquent.
Pour être honnête, je ne connais pas grand-chose à la désérialisation ou à ysoserial, mais j'ai découvert qu'il existe un ensemble d'octets dans la charge utile qui représentent la longueur de la partie "importante" de la charge utile, y compris la commande définie par l'utilisateur. Cela peut être vu dans la capture d'écran suivante d'un dump hexadécimal côte à côte de deux charges utiles différentes créées par ysoserial, chacune contenant une commande différente à exécuter :

Pour des raisons que je ne peux expliquer, ces octets reflètent la longueur de la charge utile en base 128 ; le 0x05 est multiplié par 128 pour donner 640 ; cela est ajouté à la valeur de l'autre octet (0xb0 ou 0xd4) moins 128 ; j'ai constaté que lorsque le deuxième octet (0x05) dépasse (0x06), le premier octet recommence à 0x80 au lieu de 0x00 comme on pourrait s'y attendre.
La charge utile utilisée dans cet outil ressemble à ceci :

En convertissant toutes les valeurs en décimal, le calcul ressemble à ceci :
(6 x 128) + (180-128) = 820
La taille du fichier est de 1042 ; j'ai constaté lors des tests qu'il y a (avec ce gadget d'ysoserial en tout cas) toujours une différence de 222 octets entre la taille du fichier et la longueur reflétée dans cet ensemble d'octets. Ces 222 octets semblent être "l'en-tête de la charge utile".
Les deux parties de la charge utile sont copiées dans main.c, où la longueur originale de 820 est également définie comme variable. La longueur de la commande utilisateur est calculée, et cette valeur est ajoutée au 820 original (en plus d'un seul octet supplémentaire pour un espace qui doit être inséré) afin de calculer la valeur finale qui doit être représentée dans cet ensemble d'octets. Le deuxième octet est calculé en divisant LenBytes par 128, et le premier octet est calculé en trouvant le reste de LenBytes et en ajoutant 128 (afin de prendre en compte le fait qu'il commence à 0x80).

Une fois ces octets définis, ils sont copiés dans le tampon Before à l'endroit approprié afin de définir la longueur de la charge utile à la valeur correcte compte tenu du contenu. Le tampon final est ensuite assemblé :

La charge utile finalisée est ensuite écrite sur le disque et mmc.exe est appelée pour l'exécuter. Ensuite, la charge utile est nettoyée.
x86_64-w64-mingw32-gcc -o EventViewerUAC.x64.o -Os -c main.c -DBOF
Ysoserial propose une option --rawcmd où vous pouvez invoquer un exécutable différent de cmd.exe, mais vous ne pouvez pas lui fournir d'arguments. Cela ouvre la possibilité d'invoquer directement votre charge utile sans appeler cmd.exe et donc sans laisser la fenêtre console ouverte ; cependant, rien ne fermerait mmc.exe dans ce cas. On pourrait écrire ses charges utiles pour déterminer le processus parent de la charge utile et s'il s'agit de mmc.exe, tuer mmc.exe afin de nettoyer après ce contournement UAC. C'est tout à fait possible, mais je n'ai pas pris la peine de le faire car ce n'est qu'un des nombreux contournements UAC existants, et tout le monde ne crée pas ses propres charges utiles personnalisées de cette manière. Je pourrais l'incorporer dans mes propres runners.