Analyse forensique de TaskCache : Tree, Tasks, DynamicInfo
Ce que la clé TaskCache de la ruche SOFTWARE stocke pour chaque tâche planifiée : Id/Index/SD, DynamicInfo, blobs Actions et Triggers, et Hash.
En bref. TaskCache se compose de deux moitiés. Tree\<chemin> associe un nom de tâche à son GUID (Id), à une catégorie (Index) et à un descripteur de sécurité (SD). Tasks\{GUID} stocke Path, URI, Author, Date, Hash, Actions, Triggers et DynamicInfo, dont les FILETIME donnent en UTC la création, la dernière exécution et la dernière exécution réussie. Les structures binaires ont été rétro-ingéniérées par la communauté : fiez-vous aux champs bien corroborés et recoupez le reste.
Cet article est la plongée côté registre de la série qui commence avec le guide d'analyse forensique des tâches planifiées. Les sources principales sont l'analyse de cyber.wtf (Windows 10 1909, vérifiée sur Windows 7) et le winreg-kb de libyal. Microsoft ne documente pas ces valeurs.
Organisation de la clé
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache
├── Tree\<folder>\<task name> Id, Index, SD
├── Tasks\{GUID} Path, URI, Author, Date, Hash, Actions, Triggers, DynamicInfo, ...
├── Boot\{GUID}
├── Logon\{GUID}
├── Plain\{GUID}
└── Maintenance\{GUID}
Tree reproduit l'arborescence des dossiers de C:\Windows\System32\Tasks. Tasks est à plat, avec une clé par GUID de tâche. Les quatre clés de catégorie contiennent une sous-clé par GUID de tâche appartenant à cette catégorie.
Tree : noms, catégories, permissions
| Valeur | Type | Signification |
|---|---|---|
Id | Chaîne | Le GUID de la tâche, par ex. {8A1B...}, qui pointe vers Tasks\{GUID} |
Index | DWORD | Catégorie. cyber.wtf donne 1 Boot, 2 Logon, 3 Plain, 4 Maintenance ; les dossiers n'ont pas d'Index |
SD | Binaire | Descripteur de sécurité auto-relatif qui contrôle l'accès à la tâche |
Trois points à vérifier sur chaque entrée de Tree :
SDest-il présent ? Une clé de tâche sansSDest invisible pourschtaskset pour la console. C'est la technique de dissimulation de Tarrask (Microsoft, 2022).- Le SD décodé refuse-t-il la lecture ? Binary Defense a montré qu'un descripteur valide refusant la lecture à tout le monde cache une tâche tout aussi bien, tout en ayant l'air normal (Binary Defense). Lisez le SDDL, ne vous contentez pas de constater sa présence.
- L'
Indexest-il plausible ? MITRE cite la modification de la valeurIndexcomme autre méthode de dissimulation (T1053.005). Une valeur 0 sur une tâche, ou unIndexqui contredit la clé de catégorie sous laquelle figure le GUID, mérite d'être noté.
La date de dernière écriture de la clé Tree est un FILETIME UTC mis à jour dès qu'une valeur de cette clé change. Pour une tâche normale, elle est proche de l'enregistrement. Si elle est nettement postérieure à l'heure de création indiquée par DynamicInfo, quelque chose a réécrit la clé ; sur une tâche de type Tarrask, cela peut correspondre au moment où le SD a été supprimé. C'est une déduction, pas un enregistrement direct : formulez-le ainsi dans un rapport.
Tasks{GUID} : les données de la tâche
| Valeur | Signification |
|---|---|
Path | Chemin de la tâche, par ex. \Microsoft\Windows\Defrag\ScheduledDefrag |
URI | Identique à l'URI du XML, lorsqu'il existe |
Author, Description, Source, Version, Documentation | Copies des champs de RegistrationInfo |
Date | Chaîne de date d'enregistrement, au même format sans fuseau horaire que dans le XML |
Hash | Hash d'intégrité du fichier XML, voir plus bas |
Schema | Version du schéma, sous forme numérique |
SecurityDescriptor | SDDL facultatif issu du XML |
Actions | Sérialisation binaire des actions |
Triggers | Sérialisation binaire des déclencheurs, du principal et des paramètres |
DynamicInfo | Enregistrement binaire de l'état d'exécution |
Toutes les valeurs n'existent pas pour toutes les tâches. cyber.wtf note que seules Actions et Triggers semblent nécessaires pour que la tâche apparaisse dans la console.
DynamicInfo
DynamicInfo est la raison pour laquelle il faut toujours analyser TaskCache. Voici sa structure telle que décrite par cyber.wtf, cohérente avec les indications de taille de winreg-kb (28 octets sous Vista/7, 36 octets à partir de Windows 8) :
| Offset | Taille | Champ |
|---|---|---|
| 0 | 4 | Version / magic, généralement 3 |
| 4 | 8 | FILETIME : création (enregistrement ou dernière mise à jour) |
| 12 | 8 | FILETIME : dernière exécution |
| 20 | 4 | État de la tâche |
| 24 | 4 | Code du dernier résultat (HRESULT, par ex. 0x80070002 fichier introuvable) |
| 28 | 8 | FILETIME : dernière exécution réussie (Windows 8 et ultérieurs uniquement) |
winreg-kb qualifie les deux premiers horodatages d'« unknown » et suggère « last registered or update time? » et « launch time? » ; cyber.wtf les nomme comme ci-dessus. En pratique, la valeur « création » change lorsqu'une tâche est réenregistrée : lisez-la donc comme le dernier enregistrement plutôt que comme la création initiale. Le code du dernier résultat est souvent le champ le plus utile : une tâche de persistance qui s'est exécutée correctement affiche 0x0 ; une tâche dont le binaire a été supprimé affiche un HRESULT de fichier introuvable.
Les trois FILETIME sont en UTC. Ils servent donc de point d'ancrage pour la Date du XML, qui n'a pas de fuseau horaire, comme expliqué dans l'anatomie du XML des tâches.
Le blob Actions
La valeur Actions commence par un mot de version, suivi de la chaîne du contexte du principal, puis d'un enregistrement par action identifié par une valeur magic (cyber.wtf) :
| Magic | Action |
|---|---|
0x6666 | Exec : commande, arguments, répertoire de travail |
0x7777 | ComHandler : CLSID et données |
0x8888 | SendEmail |
0x9999 | ShowMessage |
Les chaînes sont en UTF-16, précédées de leur longueur. Quand le fichier XML a disparu, c'est ce blob qui permet encore de savoir ce que la tâche exécutait. Comparez-le avec le XML quand les deux existent : un attaquant qui modifie directement le registre peut changer l'action sans toucher au fichier, ou inversement.
Le blob Triggers
Triggers commence par un en-tête (version, puis un « job bucket » contenant des flags, un CRC32, l'identifiant du principal, des informations sur l'utilisateur et des paramètres facultatifs), suivi d'enregistrements de déclencheurs de différents types, chacun avec son propre magic et un alignement sur 8 octets. La structure de l'en-tête est raisonnablement bien comprise ; celle des enregistrements de déclencheurs varie selon la version de Windows et reste moins établie. Un parseur peut extraire de façon fiable l'en-tête et le principal (le compte sous lequel la tâche s'exécute) ; considérez les détails des déclencheurs décodés depuis le blob comme un meilleur effort, et privilégiez le XML quand vous l'avez.
Hash
La valeur Hash permet au service de détecter qu'un fichier XML a été modifié en dehors de son contrôle. winreg-kb l'indique comme un SHA-256, ou un CRC32 sur les systèmes antérieurs à la mise à jour KB2305420. Sur les systèmes Windows 10 et 11 actuels, on observe qu'il s'agit du SHA-256 du contenu du fichier XML sans son BOM UTF-16 de deux octets.
Le recalculer offre un contrôle d'intégrité peu coûteux :
- Correspondance : le XML sur disque est bien celui que le service a enregistré.
- Divergence : le fichier a été modifié après l'enregistrement, ou bien l'entrée de registre, ou encore le fichier a été restauré depuis un autre emplacement. Dans tous les cas, les deux copies ne concordent pas : lisez les deux.
- Pas de XML : le fichier a été supprimé ; le registre est la seule définition restante.
Corréler TaskCache avec les fichiers XML
Associez chaque clé Tree au fichier XML situé au même chemin relatif sous System32\Tasks. Dressez ensuite la liste suivante :
| Situation | Explication habituelle | Que faire |
|---|---|---|
| XML et TaskCache, hash concordant | Normal | Trier sur le contenu |
| TaskCache seul (pas de XML) | XML supprimé à la main ou par un outil de nettoyage ; la tâche peut encore s'exécuter | Priorité haute : décoder Actions |
| XML seul (pas de TaskCache) | Fichier déposé mais jamais enregistré, registre nettoyé, ou ruche « dirty » | Vérifier l'état de la ruche et les journaux d'événements |
| Tree sans SD | Tâche cachée (type Tarrask) | Priorité haute |
| Hash divergent | Fichier ou registre modifié après l'enregistrement | Comparer les deux définitions |
Une réserve avant de déclarer quoi que ce soit « manquant » : si la ruche SOFTWARE a été copiée à chaud sans rejouer ses journaux de transactions, des modifications récentes du registre peuvent en être absentes. Voir emplacements et acquisition des tâches planifiées.
Le Scheduled Tasks Parser réalise cette corrélation, recalcule le hash, décode le SD en SDDL ainsi que DynamicInfo, Actions et l'en-tête de Triggers, et signale les tâches présentes uniquement dans le registre, uniquement sur disque ou dépourvues de SD. Pour un travail plus poussé sur le registre, le Registry parser couvre le reste de la ruche.
FAQ
Qu'est-ce que la clé de registre TaskCache ?
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache est l'endroit où le service Planificateur de tâches conserve sa copie de chaque tâche enregistrée : noms et descripteurs de sécurité sous Tree, données de la tâche sous Tasks\{GUID}, et listes par catégorie sous Boot, Logon, Plain et Maintenance.
Quels horodatages contient DynamicInfo ?
Sous Windows 8 et versions ultérieures, la valeur fait généralement 36 octets : un DWORD de version, un FILETIME couramment interprété comme l'heure de création ou d'enregistrement de la tâche, un FILETIME de dernière exécution, un DWORD d'état de la tâche, un code du dernier résultat et un FILETIME de dernière exécution réussie. Cette structure provient de recherches communautaires, pas de la documentation Microsoft.
Qu'est-ce que la valeur Hash dans TaskCache\Tasks ?
Un hash d'intégrité du XML de la tâche. Sur les systèmes actuels, on observe qu'il s'agit du SHA-256 du fichier XML sans son indicateur d'ordre des octets (BOM) de deux octets ; les systèmes plus anciens utilisaient CRC32. Une divergence avec le XML sur disque signifie que l'un des deux a changé après l'enregistrement.