
Scanner de callstack qui identifie les IOCs d'agents C2 dépaquetés ou injectés en analysant le comportement d'inactivité des threads, la mémoire non adossée, le module stomping, les APCs, les timers et l'usurpation d'adresse de retour.
Ce projet est (principalement) un scanner de callstack qui tente d'identifier des IOCs indiquant un agent C2 dépaqueté ou injecté.
Tous les contrôles sont basés sur l'observation que les agents C2 attendent entre leurs callbacks, ce qui fait que le thread du beacon est inactif, et cet outil vise à analyser ce qui a potentiellement causé l'inactivité du thread.
Cela inclut les IOCs traditionnels, tels que la mémoire non adossée (unbacked memory) ou les modules stompés (stomped modules), mais tente également de détecter de multiples implémentations de sleepmasks utilisant des APCs ou des Timers. Ce dernier est effectué à la fois en analysant la callstack mais aussi en énumérant les timers et leurs callbacks exacts depuis l'espace utilisateur (userland).
(Presque) aucun de ces IOCs ne peut être considéré comme un vrai positif à 100 %, la détection de module stompé par exemple est très sujette aux faux positifs. Cependant, les résultats peuvent éveiller des soupçons sur le comportement d'un processus.
Les binaires DotNet et 32 bits sont ignorés.

Une page privée r(w)x dans une callstack peut indiquer un beacon qui a été dépaqueté ou injecté à l'exécution.
Plusieurs Sleepmasks modifient les permissions de la page du beacon en non exécutable. Cela conduit à une page non exécutable suspecte dans la callstack.
Souvent, les beacons évitent les pages mémoire privées en chargeant et en écrasant un module légitime depuis le disque.
Grâce au mécanisme de copy on write, les images manipulées peuvent être identifiées en vérifiant le champ VirtualAttributes.SharedOriginal de MEMORY_WORKING_SET_EX_INFORMATION. Si une page dans la callstack n'est pas privée et que SharedOriginal == 0, elle est considérée comme un IOC.
C'est probablement la détection la plus sujette aux faux positifs. :'(
De multiples implémentations de sleepmasks mettent en file d'attente une série d'APC vers Ntdll!NtContinue dont l'une déclenche l'exécution de Ntdll!WaitForSingleObject. Ainsi, si Ntdll!KiUserApcDispatcher peut être trouvé sur la callstack d'une fonction bloquante, cet outil le considère comme un IOC.
Similaire à l'utilisation suspecte des APC, cet outil vérifie également la présence de ntdll!RtlpTpTimerCallback sur la callstack d'une fonction bloquante pour détecter les sleepmasks basés sur des timers.
À ma connaissance, les timers sont implémentés au-dessus des ThreadPools. Comme Alon Leviev l'a démontré, ceux-ci peuvent être énumérés en utilisant NtQueryInformationWorkerFactory avec WorkerFactoryBasicInformation.
La structure WORKER_FACTORY_BASIC_INFORMATION intègre un FULL_TP_POOL qui à son tour pointe vers une liste doublement chaînée TimerQueue. Parcourir cette liste de PFULL_TP_TIMER permet d'accéder à chaque callback enregistré. Si un callback est trouvé pointant vers un ensemble d'appels d'API suspects, comme ntdll!ntcontinue, cela peut être considéré comme un IOC fort.

À l'origine, le proxying de module a été introduit comme une méthode pour contourner les callstacks suspects. Bien que le contournement fonctionne, il introduit un autre IOC fort, car NTAPI est utilisé pour appeler WINAPI. C'est étrange, car WINAPI est une abstraction de NTAPI. Ainsi, si une callstack est observée dans laquelle une séquence de ntdll.dll->kernel32.dll->ntdll.dll se termine par l'appel d'une fonction bloquante, elle peut être considérée comme un IOC.
La plupart des implémentations d'usurpation d'adresse de retour dont j'ai connaissance utilisent une technique dans laquelle la fonction appelée retourne vers un gadget jmp [Nonvolatile-Register]. Ce projet itère simplement chaque adresse de retour dans les callstacks et recherche des motifs indiquant le retour vers un gadget jmp.

_ _ _____ ______
| | | | / ___| | ___ \
| |_| | \ `--. | |_/ /
| _ | `--. \ | ___ \
| | | | /\__/ / | |_/ /
\_| |_/ \____/ \____/
Hunt-Sleeping-Beacons | @thefLinkk
-p / --pid {PID}
--dotnet | Set to also include dotnet processes. ( Prone to false positivies )
--commandline | Enables output of cmdline for suspicious processes
-h / --help | Prints this message?