introduction
Dans cet article, nous examinerons le concept de détournement de DLL et comment il peut être utilisé pour obtenir la persistance de l'espace utilisateur sur les systèmes Windows. Cette méthode est décrite dans MITRE ATT & CK sous: "Interception de l'ordre de recherche DLL (T1038) ".
L'usurpation de DLL peut être utilisée par des attaquants à de nombreuses fins différentes, mais cet article se concentrera sur l'obtention de la résilience à l'aide d'applications à démarrage automatique. Par exemple, comme Slack et Microsoft Teams sont lancés au démarrage (par défaut), l'usurpation de DLL dans l'une de ces applications permettrait à un attaquant d'obtenir un accès robuste à sa cible - chaque fois qu'un utilisateur se connecte.
Après avoir présenté le concept des DLL, de l'ordre de recherche des DLL et de l'usurpation de DLL, je vous guiderai à travers le processus d'automatisation de la détection des interceptions DLL . Cet article traite de la détection des chemins d'interception DLL dans Slack, Microsoft Teams et Visual Studio Code.
Enfin, j'ai découvert plusieurs chemins d'interception DLL utilisés par différentes applications, recherché la cause première et constaté que les applications utilisant certains appels d'API Windows sont sujettes à l'interception DLL lorsqu'elles ne s'exécutent pas à partir de
C:\Windows\System32\
.
Je tiens à remercier mon collègue Josiah Massari (
@Airzero24
) d'avoir été le premier à repérer certains de ces hooks DLL, à expliquer leur méthodologie et à m'inspirer pour automatiser la détection.
Qu'est-ce qu'une DLL?
Une DLL est une bibliothèque contenant du code et des données qui peuvent être utilisées simultanément par plusieurs programmes. ( Source ) La
fonctionnalité d'une DLL peut être utilisée par une application Windows en utilisant l'une des fonctions
LoadLibrary*
. Les applications peuvent référencer des DLL conçues spécifiquement pour ces applications ou des DLL Windows déjà présentes sur le disque dans System32. Les développeurs peuvent charger des DLL à partir de System32 pour utiliser les fonctionnalités déjà implémentées dans Windows dans leurs applications sans avoir à écrire cette fonctionnalité à partir de zéro.
Par exemple, un développeur qui a besoin de faire des requêtes HTTP peut utiliser la bibliothèque WinHTTP (
winhttp.dll
) au lieu d'implémenter des requêtes HTTP à l'aide de sockets bruts.
Ordre de recherche et interception des DLL
Étant donné que les DLL existent sous forme de fichiers sur le disque, vous vous demandez peut-être comment une application sait où charger la DLL? Microsoft a documenté l'ordre de recherche des DLL en détail ici .
À partir de Windows XP SP2, le mode de recherche sans échec DLL est activé par défaut (
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
). Lorsque le mode sans échec est activé, l'ordre de recherche des DLL est le suivant:
- Le répertoire à partir duquel l'application a été chargée.
- Répertoire système. Utilisez la fonction GetSystemDirectory pour obtenir le chemin d'accès à ce répertoire.
- Répertoire système 16 bits. Il n'y a pas de fonction qui fournit un chemin vers ce répertoire, mais il est recherché.
- Répertoire Windows. Utilisez la fonction GetWindowsDirectory pour obtenir le chemin d'accès à ce répertoire.
- Répertoire actuel.
- , PATH. , , App Paths. App Paths DLL.
Un système peut contenir plusieurs versions de la même DLL. Les applications peuvent contrôler le choix de l'emplacement à partir duquel la DLL doit être chargée en spécifiant le chemin d'accès complet ou en utilisant un autre mécanisme tel qu'un manifeste. ( Source )
Si l'application ne spécifie pas où charger la DLL, Windows utilise l'ordre de recherche de DLL par défaut indiqué ci-dessus. La première position dans l'ordre de recherche des DLL (le répertoire à partir duquel l'application est chargée) intéresse les attaquants.
Si le développeur de l'application a l'intention de charger la DLL à partir de
C:\Windows\System32
, mais ne l'a pas explicitement écrite dans l'application, la DLL malveillante placée dans le répertoire de l'application sera chargée avant la DLL légitime de System32. Le chargement d'une DLL malveillante est appelé usurpation de DLL (ou interception) et est utilisé par les attaquants pour charger du code malveillant dans des applications approuvées / signées.
Utilisation de l'usurpation de DLL pour atteindre la résilience
L'usurpation de DLL peut être utilisée pour obtenir la résilience lorsqu'une application / service vulnérable est démarré et qu'une DLL malveillante est placée dans un emplacement vulnérable. Un de mes collègues a
@Airzero24
découvert l'usurpation de DLL dans Microsoft OneDrive, Microsoft Teams et Slack comme userenv.dll
.
Ce sont ces programmes qui sont devenus la cible de l'interception, car par défaut ils sont configurés pour démarrer au démarrage de Windows. Cela peut être vu ci-dessous dans le Gestionnaire des tâches:
Applications Windows configurées pour démarrer automatiquement
Pour tester l'usurpation de DLL, j'ai créé un chargeur de shellcode DLL qui a démarré Cobalt Strike Beacon. J'ai renommé la DLL malveillante
userenv.dll
et je l'ai copiée dans le répertoire d'application concerné. J'ai lancé l'application et j'ai vu mon nouveau rappel Beacon.
Cobalt grève Beacon Par DLL d' interception
utilisantProcess Explorer , je peux vérifier si ma DLL malveillante a été effectivement chargée par une application vulnérable.
Process Explorer montrant la DLL malveillante chargée
Détection automatique du potentiel d'interception DLL
Après avoir confirmé le piratage de DLL précédemment connu, je voulais voir si je pouvais trouver d'autres capacités d'usurpation de DLL qui pourraient être exploitées.
Le code utilisé lors de ma commande peut être trouvé ici .
Utiliser Slack comme exemple
Pour démarrer ce processus, j'ai exécuté Process Monitor (ProcMon) avec les filtres suivants:
- Nom du processus -
slack.exe
- Le résultat contient
NOT FOUND
- Le chemin se termine par
.dll
.
Recherchez les DLL manquantes dans ProcMon.
Ensuite, j'ai lancé Slack et examiné ProcMon pour toutes les DLL que Slack recherchait mais ne pouvait pas les trouver.
Chemins d'interception DLL possibles découverts par ProcMon
J'ai exporté ces données de ProcMon sous forme de fichier CSV pour faciliter l'analyse dans PowerShell.
Avec ma DLL de chargement de shellcode actuelle, je ne pouvais pas facilement comprendre les noms de DLL qui ont été chargés avec succès par Slack. J'ai créé une nouvelle DLL, qui est utilisée
GetModuleHandleEx
, et GetModuleFileName
pour déterminer le nom de la DLL chargée et l' écrire dans un fichier texte .
Mon objectif suivant était d'analyser le fichier CSV pour les chemins DLL dans la liste, afficher cette liste, copier ma DLL de test dans le chemin spécifié, démarrer le processus cible, arrêter le processus cible et supprimer la DLL de test. Si la DLL de test a été chargée avec succès, elle écrira son nom dans le fichier résultant.
Lorsque ce processus sera terminé, j'aurai une liste des détournements de DLL possibles (j'espère) écrits dans un fichier texte.
Toute la magie de mon projet DLLHijackTest est réalisée par un script PowerShell . Il accepte le chemin du fichier CSV généré par ProcMon, le chemin de votre DLL malveillante, le chemin du processus que vous souhaitez exécuter et tous les arguments que vous souhaitez transmettre au processus.
Paramètres
Get-PotentialDLLHijack Get-PotentialDLLHijack.ps1
Après quelques minutes, je vérifie le fichier texte répertorié dans ma DLL «malveillante» pour d'éventuels détournements de DLL. J'ai trouvé les chemins d'interception possibles suivants pour Slack:
PS C:Users\John\Desktop> Get-PotentialDLLHijack -CSVPath .\Logfile.CSV -MaliciousDLLPath .\DLLHijackTest.dll -ProcessPath "C:\Users\John\AppData\Local\slack\slack.exe"
C:\Users\John\AppData\Local\slack\app-4.6.0\WINSTA.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\LINKINFO.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\ntshrui.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\srvcli.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\cscapi.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\KBDUS.DLL
Utiliser Microsoft Teams comme exemple
Nous effectuons à nouveau le processus décrit ci-dessus:
- Utilisez ProcMon pour identifier les chemins d'interception DLL potentiels, exportez ces données sous forme de fichier CSV.
- Déterminez le chemin pour démarrer le processus.
- Définissez tous les arguments que vous souhaitez transmettre au processus.
- Exécutez
Get-PotentialDLLHijack.ps1
avec les arguments appropriés.
J'ai trouvé les chemins d'interception possibles suivants pour Microsoft Teams:
PS C:Users\John\Desktop> Get-PotentialDLLHijack -CSVPath .\Logfile.CSV -MaliciousDLLPath .\DLLHijackTest.dll -ProcessPath "C:\Users\John\AppData\Local\Microsoft\Teams\Update.exe" -ProcessArguments '--processStart "Teams.exe"'
C:\Users\John\AppData\Local\Microsoft\Teams\current\WINSTA.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\LINKINFO.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\ntshrui.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\srvcli.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\cscapi.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\WindowsCodecs.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\TextInputFramework.dll
Remarque : J'ai dû apporter de petites modifications au script PowerShell pour terminerTeams.exe
, car mon script tente de mettre fin au processus qu'il essayait de démarrer, dans ce cas, c'est le casUpdate.exe
.
Utilisation de Visual Studio Code comme exemple
En répétant le processus ci-dessus, j'ai trouvé les chemins d'interception potentiels suivants pour Visual Studio Code:
PS C:Users\John\Desktop> Get-PotentialDLLHijack -CSVPath .\Logfile.CSV -MaliciousDLLPath .\DLLHijackTest.dll -ProcessPath "C:\Users\John\AppData\Local\Programs\Microsoft VS Code\Code.exe"
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\WINSTA.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\LINKINFO.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\ntshrui.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\srvcli.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\cscapi.dll
Partage de DLL
J'ai remarqué que Slack, Microsoft Teams et Visual Studio Code partagent les DLL suivantes:
WINSTA.dll
LINKINFO.dll
ntshrui.dll
srvcli.dll
cscapi.dll
J'ai trouvé cela intéressant et je voulais comprendre ce qui cause ce comportement.
Méthodologie: comprendre les moyens d'intercepter les DLL partagées
J'ai regardé pile Tracy quand Slack essayé de charger
WINSTA.dll
, LINKINFO.dll
, ntshrui.dll
, srvcli.dll
et cscapi.dll
.
DLL avec chargement paresseux
j'ai remarqué des similitudes dans Tracy pile lors du chargement
WINSTA.dll
, LINKINFO.dll
, ntshrui.dll
et srvcli.dll
.
Trace de pile lorsque Code.exe tente de charger
WINSTA.dll
une trace de pile lors d'une
Teams.exe
tentative de chargement LINKINFO.dll
,
trace de pile lorsque Slack tente de charger
ntshrui.dll
une trace de pile contient constamment un appel
_tailMerge_<
dllname>_dll
, delayLoadHelper2
suivi LdrResolveDelayLoadedAPI
. Ce comportement était le même pour les trois applications.
J'ai déterminé que ce comportement est lié au chargement de DLL paresseux . À partir de la pile de trace au démarrage
WINSTA.dll
Je pouvais voir que le module responsable de ce chargement paresseux était wtsapi32.dll
.
J'ai ouvert
wtsapi32.dll
à Ghidra et utilisé Search -> For Strings -> Filter: WINSTA.dll
. Double-cliquez sur la ligne trouvée pour accéder à son emplacement en mémoire.
La ligne "
WINSTA.dll
" danswtsapi32.dll
En cliquant avec le bouton droit de la souris sur un emplacement en mémoire, nous pouvons trouver toutes les références à cette adresse.
Liens vers En
WINSTA.dll
suivant les liens, nous pouvons voir que la chaîne
WINSTA.dll
est passée à une structure nommée ImgDelayDescr
. En regardant la documentation de cette structure, nous pouvons confirmer qu'elle est liée au chargement de DLL paresseux.
typedef struct ImgDelayDescr {
DWORD grAttrs; //
RVA rvaDLLName; // RVA dll
RVA rvaHmod; // RVA
RVA rvaIAT; // RVA IAT
RVA rvaINT; // RVA INT
RVA rvaBoundIAT; // RVA IAT
RVA rvaUnloadIAT; // RVA IAT
DWORD dwTimeStamp; // 0, ,
// O.W. / DLL, (Old BIND)
} ImgDelayDescr, * PImgDelayDescr;
Cette structure peut être transmise à
__delayLoadHelper2
, qui utilisera LoadLibrary
/ GetProcAddress
pour charger la DLL spécifiée et corriger l'adresse de la fonction importée dans la table d'adresses d'importation de chargement différé (IAT).
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd, // ImgDelayDescr
FARPROC * ppfnIATEntry // IAT
);
En trouvant d'autres références à notre structure
ImgDelayDescr
, nous pouvons trouver un appel __delayLoadHelper2
qui appelle ensuite ResolveDelayLoadedAPI
. J'ai renommé le nom de la fonction, les types et les variables pour la rendre plus facile à comprendre.
__delayLoadHelper2
et ResolveDelayLoadedAPI
chez Ghidra
Excellent! Ceci est cohérent avec ce que nous avons vu dans notre trace de pile ProcMon lorsque Slack a essayé de se charger
WINSTA.dll
.
__delayLoadHelper2
et ResolveDelayLoadedAPI
dans ProcMon.
Ce comportement était uniformément pour
WINSTA.dll
, LINKINFO.dll
, ntshrui.dll
et srvcli.dll
. La principale différence entre chaque DLL à chargement différé était la DLL «parent». Dans les trois applications:
wtsapi32.dll
différé chargéWINSTA.dll
shell32.dll
paresseux chargéLINKINFO.dll
LINKINFO.dll
différé chargéntshrui.dll
ntshrui.dll
différé chargésrvcli.dll
Avez-vous remarqué quelque chose d'intéressant? On dirait qu'il
shell32.dll
télécharge LINKINFO.dll
, qui télécharge ntshrui.dll
, qui enfin télécharge srvcli.dll
. Cela nous amène à notre dernière option commune d'usurpation de DLL potentielle - cscapi.dll
.
Substitution de DLL dans NetShareGetInfo et NetShareEnum
Je regardais la trace de la pile lorsque Slack essayait de charger
cscapi.dll
et j'ai vu un appel LoadLibraryExW
qui provenait apparemment srvcli.dll
.
J'ai ouvert la
trace de
cscapi.dll
la pile au démarrage
srvcli.dll
dans Ghidra et utilisé Search -> For Strings -> Filter: cscapi.dll
. Double-cliquer sur la ligne trouvée et suivre les liens conduit à l' LoadLibrary
appel attendu .
srvcli.dll
appelle LoadLibrary pourcscapi.dll
renommer la fonction contenant l'appel
LoadLibrary
et suivre les liens, j'ai eu deux endroits où la fonction est utilisée:
Téléchargements NetShareEnum
cscapi.dll Téléchargements NetShareGetInfo
cscapi.dll
J'ai vérifié cela avec les programmes PoC qui ont appelé
NetShareEnum
et NetShareGetInfo
:
NetShareEnum.exe
téléchargements cscapi.dll
NetShareGetInfo.exe
téléchargementscscapi.dll
résultats
Les chemins d'usurpation de DLL suivants sont disponibles dans Slack:
C:\Users\John\AppData\Local\slack\app-4.6.0\WINSTA.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\LINKINFO.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\ntshrui.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\srvcli.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\cscapi.dll
C:\Users\John\AppData\Local\slack\app-4.6.0\KBDUS.DLL
Les chemins de spoofing DLL suivants sont disponibles dans Microsoft Teams:
C:\Users\John\AppData\Local\Microsoft\Teams\current\WINSTA.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\LINKINFO.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\ntshrui.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\srvcli.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\cscapi.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\WindowsCodecs.dll
C:\Users\John\AppData\Local\Microsoft\Teams\current\TextInputFramework.dll
Les chemins de spoofing DLL suivants sont disponibles dans Visual Studio Code:
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\WINSTA.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\LINKINFO.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\ntshrui.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\srvcli.dll
C:\Users\John\AppData\Local\Programs\Microsoft VS Code\cscapi.dll
En outre, j'ai constaté que les programmes utilisant
NetShareEnum
et NetShareGetInfo
offrent la possibilité de remplacer la DLL sous la forme en cscapi.dll
raison de l'appel codé en dur LoadLibrary
. J'ai vérifié ce comportement avec Ghidra et PoC.
Conclusion
Pour rappel, l'interception DLL est une méthode par laquelle les attaquants peuvent interférer avec l'exécution de code dans des applications signées / approuvées. J'ai créé des outils pour aider à automatiser la détection des chemins d'interception DLL. À l'aide de cet outil, j'ai découvert des chemins d'interception DLL dans Slack, Microsoft Teams et Visual Studio Code.
J'ai remarqué que les chemins d'interception DLL de ces trois applications se chevauchent et j'en ai recherché la cause. J'ai mis en évidence ma méthode pour comprendre cette coïncidence. J'ai appris le chargement paresseux des DLL et découvert deux appels d'API qui permettent d'intercepter les DLL dans n'importe quel programme qui les appelle:
NetShareEnum
chargescscapi.dll
NetShareGetInfo
chargescscapi.dll
Merci d'avoir pris le temps de lire cet article, j'espère que vous avez appris une chose ou deux sur les API Windows, Ghidra, ProcMon, les DLL et l'interception de DLL!
Liens
Un grand bonjour à mes collègues Daniel Heinsen (
@hotnops
), Lee Christensen ( @tifkin_
) et Matt Hand ( @matterpreter
) pour leur aide avec Ghidra / ProcMon!
Vérification des PoC publics pour une utilisation dans le pentesting