Christophe Alladoum

Plongée dans l'écosystème TTD

Cet article est le premier d'une série consacrée à la technologie Time Travel Debugging (TTD) développée par Microsoft, qui a été étudiée en détail au cours d'une récente période de recherche indépendante.

20 minutes de lectureRecherche sur la sécurité
Plongée dans l'écosystème TTD

Plusieurs fois par an, les chercheurs d'Elastic Security Labs ont la liberté de choisir et d'approfondir les projets de leur choix, seuls ou en équipe. Cette période est appelée en interne "projets de la semaine". Cet article est le premier d'une série consacrée à la technologie Time Travel Debugging (TTD) développée par Microsoft, qui a été étudiée en détail lors d'une récente session On-Week.

Bien qu'elle ait été rendue publique depuis plusieurs années, la connaissance de la DTT et de son potentiel est largement sous-estimée au sein de la communauté de l'informatique. Nous espérons que cette série en deux parties vous aidera à comprendre comment le TTD peut être utile pour le débogage de programmes, la recherche et l'exploitation de vulnérabilités et l'analyse de logiciels malveillants.

Cette recherche a consisté tout d'abord à comprendre le fonctionnement interne du TTD, puis à évaluer les utilisations intéressantes qui peuvent en être faites. Ce billet se concentrera sur la manière dont les chercheurs plongent dans la DTT, en partageant leur méthodologie ainsi que quelques résultats intéressants. La deuxième partie détaillera l'utilisation applicable du TTD aux fins de l'analyse des logiciels malveillants et de l'intégration avec Elastic Security.

Arrière-plan

Time Travel Debugging est un outil développé par Microsoft Research qui permet aux utilisateurs d'enregistrer l'exécution et de naviguer librement dans le temps d'exécution en mode utilisateur d'un binaire. Le TTD lui-même repose sur deux technologies : Nirvana pour la traduction binaire et iDNA pour le processus de lecture/écriture des traces. Disponible depuis Windows 7, les fonctionnalités internes du TTD ont été détaillées pour la première fois dans un document accessible au public. Depuis, Microsoft et des chercheurs indépendants l'ont étudiée en détail. C'est pourquoi nous n'explorerons pas en profondeur les aspects internes de ces deux technologies. Les chercheurs d'Elastic ont plutôt étudié l'écosystème - ou les exécutables, les DLL et les pilotes - qui permet à la mise en œuvre du TTD de fonctionner. Cela nous a permis de faire des découvertes intéressantes sur TTD, mais aussi sur Windows lui-même, car TTD tire parti de certaines techniques (non documentées) pour fonctionner comme prévu dans des cas particuliers, tels que les processus protégés.

Mais pourquoi enquêter sur le TTD ? Outre la curiosité pure, il est probable que l'une des utilisations possibles de cette technologie soit la découverte de bogues dans des environnements de production. Lorsque les bogues sont difficiles à déclencher ou à reproduire, le fait de disposer d'un environnement de type "enregistrer-une-fois-rejouer-toujours" permet de compenser cette difficulté, ce qui est exactement ce que TTD met en œuvre lorsqu'il est couplé à WinDbg.

Les outils de débogage tels que WinDbg ont toujours été une immense source d'informations lors de l'inversion de composants Windows, car ils fournissent des informations supplémentaires compréhensibles, généralement en texte clair. Les outils de débogage (en particulier les débogueurs) doivent coopérer avec le système d'exploitation sous-jacent, ce qui pourrait impliquer des interfaces de débogage et/ou des capacités précédemment non divulguées du système d'exploitation. TTD est conforme à ce modèle.

Vue d'ensemble

Le TTD fonctionne en créant d'abord un enregistrement qui suit chaque instruction exécutée par une application et la stocke dans une base de données (suffixe .run). Les traces enregistrées peuvent être rejouées à volonté à l'aide du débogueur WinDbg qui, lors du premier accès, indexe le fichier .run ce qui permet une navigation plus rapide dans la base de données. Pour pouvoir suivre l'exécution de processus arbitraires, TTD injecte une DLL responsable de l'enregistrement de l'activité à la demande, ce qui lui permet d'enregistrer des processus en les créant, mais aussi de s'attacher à un processus déjà en cours d'exécution.

TTD est téléchargeable gratuitement dans le cadre du paquet WinDbg Preview dans le MS Store. Il peut être utilisé directement à partir de WinDbg Preview (aka WinDbgX), mais c'est un composant autonome qui se trouve dans C:\Program Files\WindowsApps\Microsoft.WinDbg_<version></version>_<arch>__8wekyb3d8bbwe\amd64\ttd pour l'architecture x64, sur laquelle nous nous concentrerons dans ce billet. Les versions x86 et arm64 sont également disponibles en téléchargement dans le MS Store.

Le paquet se compose de deux fichiers EXE (TTD.exe et TTDInject.exe). et une poignée de DLL. Cette recherche se concentre sur la principale DLL responsable de tout ce qui n'est pas lié à Nirvana/iDNA (c.-à-d. responsable de la gestion des sessions, de la communication avec les pilotes, de l'injection de DLL, etc.) : ttdrecord.dll

Note : La plupart de ces recherches ont été effectuées en utilisant deux versions de la DLL ttdrecord : la plupart sur une version 2018 (1.9.106.0 SHA256=aca1786a1f9c96bbe1ea9cef0810c4d164abbf2c80c9ecaf0a1ab91600da6630), and early 2022 version (10.0.19041.1 SHA256=1FF7F54A4C865E4FBD63057D5127A73DA30248C1FF28B99FF1A43238071CBB5C). Les anciennes versions contenaient davantage de symboles, ce qui a permis d'accélérer le processus de rétro-ingénierie. Nous avons ensuite réadapté les structures et les noms de fonctions à la version la plus récente. Par conséquent, certaines des structures expliquées ici peuvent ne pas être les mêmes si vous essayez de les reproduire sur des versions plus récentes. _

Examen des caractéristiques de la DTT

Paramètres de la ligne de commande

Les lecteurs doivent noter que TTD.exe agit essentiellement comme une enveloppe pour ttdrecord!ExecuteTTTracerCommandLine :

HRESULT wmain()
{
v28 = 0xFFFFFFFFFFFFFFFEui64;
hRes = CoInitializeEx(0i64, 0);
if ( hRes >= 0 )
{
ModuleHandleW = GetModuleHandleW(L"TTDRecord.dll");
[...]
TTD::DiagnosticsSink::DiagnosticsSink(DiagnosticsSink, &v22);
CommandLineW = GetCommandLineW();
lpDiagnosticsSink = Microsoft::WRL::Details::Make<TTD::CppToComDiagnosticsSink,TTD::DiagnosticsSink>(&v31, DiagnosticsSink);
hRes = ExecuteTTTracerCommandLine(*lpDiagnosticsSink, CommandLineW, 2i64);
[...]

La dernière ligne de l'extrait de code ci-dessus montre un appel à ExecuteTTTracerCommandLine , qui prend un entier comme dernier argument. Cet argument correspond aux modes de traçage souhaités, qui sont : - 0 -> FullTracingMode, - 1 -> UnrestrictedTracing et - 2 -> Standalone (le mode codé en dur pour la version publique de TTD.exe).

Forcer TTD à fonctionner en mode de traçage complet révèle les options disponibles, qui comprennent des capacités cachées telles que la répartition des processus (-parent) et le traçage automatique jusqu'au redémarrage (-onLaunch) pour les programmes et les services.

L'extraction de l'ensemble des options de TTDRecord.dll a révélé des options de ligne de commande cachées intéressantes, telles que :

-persistent Trace programs or services each time they are started (forever). You must specify a full path to the output location with -out.
-delete Stop future tracing of a program previously specified with -onLaunch or -persistent. Does not stop current tracing. For -plm apps you can only specify the package (-delete <package>) and all apps within that package will be removed from future tracing
-initialize Manually initialize your system for tracing. You can trace without administrator privileges after the system is initialized.

Le processus de mise en place de Nirvana exige que TTD configure le champ InstrumentationCallback dans la cible _EPROCESS. Cela est possible grâce à l'appel système (non documenté mais connu) NtSetInformationProcess(ProcessInstrumentationCallback) (ProcessInstrumentationCallback, qui a une valeur de 40). En raison de l'implication potentielle sur la sécurité, l'invocation de ce syscall nécessite des privilèges élevés. Il est intéressant de noter que le drapeau -initialize indique également que le TTD peut être déployé en tant que service Windows. Ce service serait chargé de transmettre les demandes de traçage à des processus arbitraires. Vous pouvez le confirmer en l'exécutant et en consultant le message d'erreur qui en résulte :

Même s'il est facile detrouver des preuves confirmant l'existence de TTDService.exe, le fichier n'a pas été fourni dans le cadre du paquetage public, donc à part le fait de noter que TTD peut fonctionner en tant que service, nous ne l'aborderons pas dans ce billet.

Injection du procédé TTD

Comme expliqué, un fichier de trace TTD peut être créé à partir du binaire autonome TTD.exe ou par l'intermédiaire du service TTDService.exe (privé), qui doivent tous deux être exécutés dans un contexte privilégié. Cependant, il ne s'agit que de lanceurs et l'injection de la DLL d'enregistrement (nommée TTDRecordCPU.dll) est le travail d'un autre processus : TTDInject.exe.

TTDInject.exe est un autre exécutable sensiblement plus grand que TTD.exe, mais dont l'objectif est assez simple : préparer la session de traçage. Pour simplifier à l'extrême, TTD.exe démarre d'abord le processus à enregistrer dans un état suspendu. Il lancera alors TTDInject.exe, en lui transmettant tous les arguments nécessaires à la préparation de la session. Notez que TTDInject peut également lancer le processus directement en fonction du mode de traçage mentionné précédemment - nous décrivons donc le comportement le plus courant (c'est-à-dire lorsqu'il est lancé à partir de TTD.exe).

TTDInject créera un thread pour exécuter TTDLoader!InjectThread dans le processus enregistré, qui, après diverses validations, chargera à son tour la bibliothèque responsable de l'enregistrement de toute l'activité du processus, TTDRecordCPU.dll.

À partir de ce moment, toutes les instructions, tous les accès à la mémoire, toutes les exceptions déclenchées ou tous les états de l'unité centrale rencontrés au cours de l'exécution seront enregistrés.

Une fois que le déroulement général du TTD a été compris, il est apparu clairement que peu ou pas de manipulations sont possibles après l'initialisation de la session. Une attention particulière a donc été accordée aux arguments soutenus par ttdrecord.dll. Grâce au format des fonctions de manipulation du C++, de nombreuses informations essentielles peuvent être extraites des noms de fonctions eux-mêmes, ce qui rend l'analyse de l'analyseur d'arguments de la ligne de commande relativement simple. Un drapeau intéressant a été découvert : PplDebuggingToken. Ce drapeau est caché et n'est disponible qu'en mode illimité.

L'existence de ce drapeau a immédiatement soulevé des questions : TTD a été architecturé d'abord autour de Windows 7 et 8, et sur Windows 8.1+. Le concept de niveau de protection a été ajouté aux processus, ce qui signifie que les processus ne peuvent ouvrir des handles que vers un processus dont le niveau de protection est égal ou inférieur. Il s'agit d'un simple octet dans la structure _EPROCESS du noyau, qui n'est donc pas directement modifiable depuis le mode utilisateur.

Les valeurs de l'octet du niveau de protection sont bien connues et sont résumées dans le tableau ci-dessous.

Le sous-système de l'autorité de sécurité locale (lsass.exe) sous Windows peut être configuré pour fonctionner en tant que processus protégé léger, ce qui vise à limiter la portée d'un intrus qui obtiendrait des privilèges maximums sur un hôte. En agissant au niveau du noyau, aucun processus en mode utilisateur ne peut ouvrir un handle vers lsass, quel que soit son niveau de privilège.

Mais le drapeau PplDebuggingToken semble suggérer le contraire. Si un tel drapeau existait, ce serait le rêve de tout pentester/red teamer : un jeton (magique) qui leur permettrait de s'introduire dans des processus protégés et de les enregistrer, de vider leur mémoire, etc. L'analyseur de la ligne de commande semble impliquer que le contenu du drapeau de commande est une simple chaîne de caractères. S'agit-il d'une porte dérobée de la PPL ?

À la recherche du jeton de débogage PPL

Retour à ttdrecord.dll, l'option de ligne de commande PplDebuggingToken est analysée et stockée dans une structure de contexte avec toutes les options requises pour créer la session TTD. La valeur peut être recherchée à plusieurs endroits, dont un intéressant dans TTD::InitializeForAttach, dont le comportement est simplifié dans le pseudo-code suivant :

ErrorCode TTD::InitializeForAttach(TtdSession *ctx)
{
  [...]
  EnableDebugPrivilege(GetCurrentProcess()); // [1]
  HANDLE hProcess = OpenProcess(0x101040u, 0, ctx->dwProcessId);
  if(hProcess == INVALID_HANDLE_VALUE)
 {
    goto Exit;
  }
  [...]
  HMODULE ModuleHandleW = GetModuleHandleW(L"crypt32.dll");
  if ( ModuleHandleW )
  pfnCryptStringToBinaryW = GetProcAddress(ModuleHandleW, "CryptStringToBinaryW"); // [2]

  if ( ctx->ProcessDebugInformationLength ) // [3]
  {
DecodedProcessInformationLength = ctx->ProcessDebugInformationLength;
DecodedProcessInformation = std::vector<unsigned char>(DecodedProcessInformationLength);
wchar_t* b64PplDebuggingTokenArg = ctx->CmdLine_PplDebugToken;
if ( *pfnCryptStringToBinaryW )
{
  if( ERROR_SUCCESS == pfnCryptStringToBinaryW( // [4]
                      b64PplDebuggingTokenArg,
                      DecodedProcessInformationLength,
                      CRYPT_STRING_BASE64,
                      DecodedProcessInformation.get(),
                      &DecodedProcessInformationLength,
                      0, 0))
  {
    Status = NtSetInformationProcess( // [5]
               NtGetCurrentProcess(),
               ProcessDebugAuthInformation,
               DecodedProcessInformation.get(),
               DecodedProcessInformationLength);
  }
[...]

Après avoir activé le drapeau SeDebugPrivilege pour le processus en cours ([1]) et obtenu un identifiant du processus auquel s'attacher ([2]), la fonction résout une fonction générique exportée utilisée pour effectuer des opérations sur les chaînes de caractères : crypt32!CryptStringToBinaryW. Dans ce cas, il est utilisé pour décoder la valeur encodée en base64 de l'option de contexte PplDebuggingToken si elle a été fournie par la ligne de commande ([3], [4]). La valeur décodée est ensuite utilisée pour appeler l'appel de système NtSetInformationProcess(ProcessDebugAuthInformation) ([5]). Le jeton ne semble être utilisé nulle part ailleurs, ce qui nous a incités à examiner attentivement cet appel de service.

La classe d'information sur les processus ProcessDebugAuthInformation a été ajoutée dans le RS4. Un rapide coup d'œil à ntoskrnl montre que ce syscall transmet simplement la mémoire tampon à CiSetInformationProcess situé dans ci.dll, qui est la DLL du pilote Code Integrity. Le tampon est ensuite transmis à ci!CiSetDebugAuthInformation avec des arguments entièrement contrôlés.

Le diagramme suivant résume à un haut niveau ce qui se passe dans le flux d'exécution du TTD.

Le flux d'exécution de CiSetDebugAuthInformation est assez simple : le tampon contenant le PplDebuggingToken décodé en base64 et sa longueur sont transmis à ci!SbValidateAndParseDebugAuthToken en tant qu'arguments pour l'analyse et la validation. Si la validation réussit, et après une validation supplémentaire, un identifiant du processus exécutant l'appel syscall (rappelez-vous que nous gérons toujours l'appel syscall nt!NtSetInformationProcess) sera inséré dans un objet d'information de débogage du processus, puis stocké dans une entrée de la liste globale.

Mais en quoi cela est-il intéressant ? Parce que cette liste n'est accessible qu'à un seul endroit : dans ci!CiCheckProcessDebugAccessPolicy, et que cette fonction est atteinte lors d'un appel syscall NtOpenProcess. Et, comme le nom de l'indicateur nouvellement découvert l'a suggéré plus tôt, tout processus dont le PID se trouve dans cette liste contournera l'application du niveau de protection. Cela a été confirmé pratiquement dans une session KD en plaçant un point d'arrêt d'accès sur cette liste (sur notre version de ci.dll, il était situé à ci+364d8). Nous avons également activé PPL sur LSASS et écrit un simple script PowerShell qui déclencherait un appel syscall NtOpenProcess :

En examinant l'appel à nt!PsTestProtectedProcessIncompatibility dans nt!PspProcessOpen, nous pouvons confirmer que notre processus PowerShell tente de cibler lsass.exe, qui est un processus PPL :

Maintenant, confirmons la théorie initiale de ce que l'argument PplDebuggingToken ferait en forçant la valeur de retour de l'appel à nt!PsTestProtectedProcessIncompatibility :

Nous interrompons l'instruction qui suit l'appel à nt!PsTestProtectedProcessIncompatibility (qui appelle uniquement CI!CiCheckProcessDebugAccessPolicy), et nous forçons la valeur de retour à 0 (comme indiqué précédemment, une valeur de 1 signifie qu'il y a incompatibilité) :

Succès ! Nous avons obtenu une poignée pour LSASS malgré le fait qu'il s'agisse de PPL, ce qui confirme notre théorie. En résumé, si nous pouvons trouver une "valeur valide" (nous y reviendrons bientôt), elle passera la vérification de SbValidateAndParseDebugAuthToken() dans ci!CiSetDebugAuthInformation(), et nous aurons un contournement universel de la PPL. Si cela semble trop beau pour être vrai, c'est en grande partie parce que c'est le cas - mais pour le confirmer, il faut mieux comprendre ce que fait CI.dll.

Comprendre les politiques d'intégrité du code

Les restrictions basées sur l'intégrité du code, telles que celles utilisées par AppLocker, peuvent être appliquées par le biais de politiques qui, dans leur forme lisible par l'homme, sont des fichiers XML. Il existe deux types de polices : la police de base et la police complémentaire. Vous trouverez des exemples de politiques de base dans leur format XML à l'adresse suivante : "C:\NWindows\NSchemas\NCodeIntegrity\NExamplePolicies". Voici à quoi ressemble une politique de base sous sa forme XML (tirée de "C:\NWindows\Nschemas\NCodeIntegrity\NExamplePolicies\NAllowAll.xml"), qui révèle clairement en clair la plupart des détails qui nous intéressent.

<?xml version="1.0" encoding="utf-8"?>
<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">
<VersionEx>1.0.1.0</VersionEx>
<PolicyID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyID>
<BasePolicyID>{A244370E-44C9-4C06-B551-F6016E563076}</BasePolicyID>
<PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>
<Rules>
<Rule><Option>Enabled:Unsigned System Integrity Policy</Option></Rule>
<Rule><Option>Enabled:Advanced Boot Options Menu</Option></Rule>
<Rule><Option>Enabled:UMCI</Option></Rule>
<Rule><Option>Enabled:Update Policy No Reboot</Option></Rule>
</Rules>
<!--EKUS-- >
<EKUs />
<!--File Rules-- >
<FileRules>
<Allow ID="ID_ALLOW_A_1" FileName="*" />
<Allow ID="ID_ALLOW_A_2" FileName="*" />
</FileRules>
<!--Signers-- >
<Signers />
<!--Driver Signing Scenarios-- >
<SigningScenarios>
<SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Auto generated policy on 08-17-2015">
  <ProductSigners>
    <FileRulesRef><FileRuleRef RuleID="ID_ALLOW_A_1" /></FileRulesRef>
  </ProductSigners>
</SigningScenario>
<SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="Auto generated policy on 08-17-2015">
  <ProductSigners>
    <FileRulesRef><FileRuleRef RuleID="ID_ALLOW_A_2" /></FileRulesRef>
  </ProductSigners>
</SigningScenario>
</SigningScenarios>
<UpdatePolicySigners />
<CiSigners />
<HvciOptions>0</HvciOptions>
<Settings>
<Setting Provider="PolicyInfo" Key="Information" ValueName="Name">
  <Value><String>AllowAll</String></Value>
</Setting>
<Setting Provider="PolicyInfo" Key="Information" ValueName="Id">
  <Value><String>041417</String></Value>
</Setting>
</Settings>
</SiPolicy>

Les politiques au format XML peuvent être compilées au format binaire à l'aide de la cmdlet PowerShell ConvertFrom-CiPolicy :

Les politiques de base permettent une granularité fine, avec la possibilité de restreindre par nom, chemin, hachage ou signataire (avec ou sans EKU spécifique) ; mais aussi dans leur mode d'action (Audit ou Enforced).

Les politiques supplémentaires ont été conçues comme une extension des politiques de base afin d'offrir une plus grande flexibilité permettant, par exemple, d'appliquer (ou non) des politiques à un groupe spécifique de postes de travail ou de serveurs. Elles sont donc plus spécifiques, mais peuvent aussi être plus permissives que la politique de base ne devrait l'être. Il est intéressant de noter qu'avant 2016, les politiques complémentaires n'étaient pas liées à un appareil spécifique, ce qui permettait d'atténuer les contournements corrigés par MS16-094 et MS16-100, qui ont été largement couverts par les médias.

En gardant ces informations à l'esprit, il est possible de revenir à ci!SbValidateAndParseDebugAuthToken avec plus de clarté : la fonction suit essentiellement trois étapes : 1. Appelez ci!SbParseAndVerifySignedSupplementalPolicy pour analyser le tampon d'entrée du syscall et déterminer s'il s'agit d'une politique supplémentaire valablement signée 2. appelez ci!SbIsSupplementalPolicyBoundToDevice pour comparer le DeviceUnlockId de la politique supplémentaire à celui du système actuel ; de telles valeurs peuvent être facilement récupérées en utilisant le syscall NtQuerySystemEnvironmentValueEx avec le GUID {EAEC226F-C9A3-477A-A826-DDC716CDC0E3}3. Enfin, extrayez deux variables de la politique : un entier (DWORD) qui correspond au niveau de protection, et une (UNICODE_STRING) Autorisation de débogage.

Étant donné qu'il est possible d'élaborer des fichiers de stratégie (via XML ou un script PowerShell), l'étape 3 n'est pas un problème. L'étape 2 ne l'est pas non plus, car le DeviceUnlockId peut être forgé à l'aide de la syscall NtSetSystemEnvironmentValueEx({EAEC226F-C9A3-477A-A826-DDC716CDC0E3}), à condition de disposer du privilège SeSystemEnvironmentPrivilege. Toutefois, il convient de noter que l'UnlockId est une valeur volatile qui sera rétablie au redémarrage.

Cependant, il est pratiquement impossible de contourner l'étape, car il faut : - posséder la clé privée 1 d'un certificat appartenant à Microsoft avec l' OID particulier 1.3.6.1.4.1.311.10.3.6(c'est-à-dire MS NT5 Lab (szOID_NT5_CRYPTO)) - et que le certificat susmentionné ne soit pas révoqué ou expiré.

Qu'en est-il alors ? Nous avons maintenant confirmé que, contrairement aux idées reçues, les processus PPL peuvent être ouverts par un autre processus sans qu'il soit nécessaire de charger un pilote du noyau. Il convient toutefois de souligner qu'il s'agit d'un cas d'utilisation de niche, puisque seul Microsoft détient (littéralement) les clés permettant d'utiliser cette technique pour des machines très ciblées. Néanmoins, ce cas reste un excellent exemple d'utilisation de l'IC à des fins de débogage.

TTD offensif

Note : Pour rappel, TTD.exe nécessite des privilèges élevés, ce que toutes les techniques présentées ci-dessous supposent.

Tout au long de ces recherches, nous avons découvert des cas d'utilisation offensifs et défensifs potentiellement intéressants de la TTD.

Traçage != Débogage

TTD n'est pas un débogueur ! Par conséquent, il fonctionnera parfaitement sans être détecté pour les processus qui effectuent une vérification anti-débogage de base, comme l'utilisation de IsDebuggerPresent() (ou toute autre méthode qui dépend de PEB.BeingDebugged). La capture d'écran suivante illustre ce détail en rattachant le TTD à un simple processus de bloc-notes :

À partir d'un débogueur, nous pouvons vérifier le champ BeingDebugged situé dans le bloc-notes PEB, qui montre que le drapeau n'est pas activé :

Le cas curieux de ProcLaunchMon

Une autre astuce intéressante mise à disposition par TTD consiste à abuser du pilote Windows intégré ProcLaunchMon.sys. Lorsqu'il fonctionne en tant que service (c'est-à-dire TTDService.exe), ttdrecord.dll créera l'instance de service, chargera le pilote et communiquera avec le dispositif disponible à .\com_microsoft_idna_ProcLaunchMon pour enregistrer les nouveaux clients suivis.

Le pilote lui-même sera utilisé pour surveiller les nouveaux processus créés par le service TTD et suspendre ces processus directement à partir du noyau, contournant ainsi toute protection qui surveille uniquement la création de processus avec l'indicateur de création CREATE_SUSPENDED (comme mentionné ici par exemple). Pour cette recherche, nous avons développé un client de base pour le pilote de périphérique, que vous trouverez ici.

CreateDump.exe

Autre fait amusant : bien qu'il ne fasse pas strictement partie de TTD, le paquet WinDbgX fournit un binaire signé .NET dont le nom résume parfaitement sa fonctionnalité : createdump.exe. Ce binaire est situé à l'adresse "C:\NProgram Files\NWindowsApps\NMicrosoft.WinDbg_*\Ncreatedump.exe".

Ce binaire peut être utilisé pour faire un snapshot et un dumping du contexte d'un processus fourni en argument, dans la lignée directe d'autres LOLBAS.

Cela souligne une fois de plus la nécessité de ne pas s'appuyer sur des signatures statiques et des entrées de liste de blocage de noms de fichiers pour se protéger contre des attaques telles que le credential dumping et de privilégier des approches plus robustes telles que RunAsPPL, Credential Guard ou le Credential Hardening d'Elastic Endpoint.

TTD défensif

Blocage du TTD

Bien que la fonction TTD soit extrêmement utile, les cas où elle devrait être activée sur des machines autres que de développement ou de test (telles que des serveurs ou des postes de travail de production) sont rares. Même si cela semble peu documenté au moment de la rédaction du présent document, ttdrecord.dll permet un scénario de sortie anticipée en créant ou en mettant à jour une clé de registre située sous "HKEY_LOCAL_MACHINE\Software\Microsoft\TTD", et en mettant à jour la valeur DWORD32 RecordingPolicy à 2. Les tentatives ultérieures d'utilisation de n'importe quel service TTD (TTD.exe, TTDInject.exe, TTDService.exe) sera arrêté et un événement ETW sera généré pour suivre les tentatives.

Détection du TTD

Prévenir l'utilisation de la RTT pourrait être trop extrême pour tous les environnements - cependant, il existe plusieurs indicateurs permettant de détecter l'utilisation de la RTT. Un processus en cours de traçage possède les propriétés suivantes :

  • Un thread exécutera le code de TTDRecordCPU.dll, ce qui peut être vérifié à l'aide d'une simple commande Windows intégrée : tasklist /m TTDRecordCPU.dll
  • Même si cela peut être contourné, le PID parent du processus enregistré (ou le premier, dans le cas où le traçage récursif est activé), serait TTD.exe lui-même :

  • En outre, le pointeur _KPROCESS.InstrumentationCallback doit être placé dans la section BSS de TTDRecordCPU.dll de l'exécutable :

Par conséquent, la détection des traces de TTD peut être réalisée à la fois par des méthodes en mode utilisateur et en mode noyau.

Conclusion

Ceci conclut la première partie de cette recherche "en cours de semaine" axée sur la RTT. En creusant dans les rouages de l'écosystème TTD, nous avons découvert des mécanismes très intéressants et moins connus, intégrés à Windows, qui sont nécessaires pour que TTD fonctionne dans certains cas particuliers, tels que le suivi des processus PPL.

Bien que cette recherche n'ait pas dévoilé une nouvelle porte dérobée secrète permettant de cibler les processus PPL, elle a mis en évidence une technique inexplorée intégrée à Windows permettant de le faire. Cette recherche souligne l'importance d'un modèle basé sur une cryptographie forte (ici par le biais de CI.dll), et la manière dont il peut apporter beaucoup de flexibilité - tout en maintenant un niveau élevé de sécurité - lorsqu'il est mis en œuvre de manière adéquate.

La deuxième partie de cette série sera moins axée sur la recherche et plus pratique avec la sortie d'un petit outil que nous avons également développé dans le cadre de la semaine de travail. Cela facilite le processus d'analyse binaire par TTD, en utilisant le bac à sable de Windows.

Remerciements

Alors que cette recherche était déjà terminée et que l'article était en cours de rédaction, l'auteur a pris connaissance d'une recherche portant sur un sujet similaire et de résultats concernant cette même technique (jeton de débogage PPL). Ces recherches ont été menées par Lucas George (de la société Synacktiv), qui a présenté ses conclusions lors de SSTIC 2022.

Partager cet article