Reconstruire une chronologie Jump List quand on a cherché à l'effacer
La plupart des articles traitent une Jump List comme un journal trié qu'on lit de haut en bas. Ce n'en est pas un. Le flux DestList est une structure des éléments les plus récemment utilisés, et à l'instant où vous supposez que l'ordre dans le fichier est l'ordre des événements, votre chronologie est fausse et vous ne le savez pas. Cet article porte sur le fait d'obtenir le bon ordre, et sur ce à quoi ressemble l'artefact après que quelqu'un est passé par là pour tenter de faire disparaître un problème.
Si vous n'en avez jamais analysé une, commencez par le guide DFIR et la décomposition du format de fichier automaticDestinations. On part ici du principe que vous savez déjà ce que contient une ligne DestList.
Le piège de l'ordre
Chaque entrée DestList porte un seul FILETIME : la dernière fois que le shell a enregistré l'ouverture de cette cible. Une seule valeur. Elle est écrasée à chaque accès. Donc une entrée vous dit la dernière ouverture et rien des cinq ouvertures précédentes. C'est la première chose que les gens se trompent. Ils voient "dernier accès 14:07" et écrivent "l'utilisateur a ouvert le fichier une fois à 14:07" dans le rapport. Le fichier a peut-être été ouvert quarante fois. Vous ne voyez jamais que la dernière.
La deuxième chose qu'ils se trompent, c'est la séquence. Les flux LNK numérotés (1, 2, 3, et ainsi de suite) sont alloués à peu près dans l'ordre de création, mais ils ne sont pas non plus votre chronologie, car Windows les réutilise et les réécrit. Ce que vous voulez réellement, ce sont deux ordres indépendants, triés puis comparés :
- Le FILETIME de dernier accès par entrée, trié par ordre croissant. C'est votre ordre des événements.
- Les valeurs d'ID d'entrée du DestList. L'en-tête conserve un compteur que le shell incrémente chaque fois qu'il attribue une nouvelle entrée. Chaque ligne stocke l'ID qui lui a été donné. Ce compteur ne fait que monter, donc les ID d'entrée sont un enregistrement monotone de l'ordre dans lequel les cibles sont entrées dans la liste pour la première fois.
Quand ces deux ordres concordent, vous avez une MRU propre et vous pouvez faire confiance à la chronologie. Quand ils divergent, il s'est passé quelque chose d'intéressant, et la divergence est exactement le signal qui occupe le reste de cet article.
Reconstruire la séquence
Analysez vers du CSV et extrayez trois colonnes : ID d'entrée, date de dernier accès, et chemin cible. Avec JLECmd :
JLECmd.exe -d "C:\case\AutomaticDestinations" --csv "C:\case\out"
Triez ensuite par LastModified (le nom que JLECmd donne au FILETIME du DestList) et gardez EntryNumber à côté. Dans une liste normale, les ID d'entrée évolueront dans le même sens que les horodatages. Un fichier ouvert récemment se trouve en haut de la MRU et porte un ID d'entrée élevé et un horodatage récent. Les cibles anciennes et non touchées portent des ID bas et des horodatages anciens et dérivent vers l'éviction.
Regardez maintenant le compteur de l'en-tête face au nombre de lignes. Si l'en-tête du DestList indique que le dernier numéro d'entrée attribué est 61 et que vous tenez neuf lignes, une cinquantaine d'entrées ont quitté cette liste à un moment donné. Avant d'écrire "des preuves ont été supprimées", comprenez d'abord l'explication ennuyeuse : la Jump List automatique est plafonnée. Le plafond est lié au paramètre shell "nombre d'éléments récents à afficher", et lorsque la liste est pleine, l'entrée la moins récemment utilisée est évincée pour faire de la place. Un roulement normal produit exactement ce trou. Un poste de travail qui ouvre des documents toute la journée affichera un compteur élevé et une liste survivante courte, et ce n'est qu'un mardi comme un autre.
Ce qui n'est pas normal, ce sont des trous dans les ID survivants qui brisent la logique MRU, ou un compteur d'en-tête qui ne bouge pas pendant que les dates d'accès continuent d'avancer. Gardez cette idée en tête.
À quoi ressemble vraiment une Jump List vidée
Les utilisateurs et les attaquants attrapent les mêmes instruments grossiers. Le bouton de réglage ("afficher les éléments récemment ouverts dans le menu Démarrer, les Jump Lists et l'Explorateur de fichiers"), la suppression des fichiers par AppID, ou l'exécution d'un nettoyeur de confidentialité. Le guide pour vider ou supprimer couvre la mécanique. Voici le résidu forensique que chacun laisse.
Supprimez purement et simplement le fichier .automaticDestinations-ms et Windows ne le pleure pas. La prochaine utilisation interactive de cette application recrée le fichier de zéro. Donc l'absence est rarement une absence. Ce que vous trouvez, c'est un fichier dont les dates de création $STANDARD_INFORMATION et $FILE_NAME dans la MFT sont étrangement récentes, contenant une poignée d'entrées dont les horodatages d'accès se regroupent tous dans une fenêtre étroite après cette date de création. Une Jump List Notepad qui prétend que tout l'historique de l'utilisateur avec Notepad a commencé jeudi dernier en début d'après-midi, sur une machine provisionnée il y a deux ans, vous dit quand l'ancienne est morte, pas quand l'utilisateur a commencé à utiliser Notepad.
Le bouton de réglage est plus sournois parce qu'il tronque au lieu de détruire, et le fichier conserve sa date de création d'origine. L'indice là, c'est un DestList court et plat : peu d'entrées, toutes regroupées récemment, un compteur d'ID d'entrée qui réinitialise l'histoire. Recoupez l'empreinte du bouton lui-même dans le registre (HKCU\...\Explorer\Advanced\Start_TrackDocs) et le parseur de registre datera le changement pour vous via la date de dernière modification de la clé.
La suppression elle-même est enregistrée à un endroit auquel la personne ne pense presque jamais à nettoyer. Les écritures dans le fichier Jump List passent par le journal des changements NTFS. Extrayez le journal USN et vous verrez les drapeaux de raison DataOverwrite, DataExtend, et souvent FileDelete/FileCreate face au nom .automaticDestinations-ms, horodatés. La Jump List peut avoir disparu et le journal indique tout de même quand elle est partie.
La falsification chirurgicale, et pourquoi elle est facile à attraper
La suppression de masse est bruyante. Le geste plus discret consiste à ouvrir le fichier composé et à retirer une entrée gênante en laissant le reste intact. C'est là que connaître le format du conteneur paie.
Le .automaticDestinations-ms est un fichier composé OLE (CFBF, l'ancien format de stockage structuré). Il a une FAT, un répertoire de flux nommés, et de l'espace résiduel de secteur comme toute structure d'allocation. Deux façons dont la chirurgie tourne mal :
Un éditeur improvisé retire le flux LNK numéroté mais ne touche pas au DestList. Vous avez maintenant une ligne DestList dont l'entrée référence un numéro de flux qui n'est pas présent dans le répertoire. Un pointeur orphelin. Analysez les flux, analysez le DestList, comparez les deux ensembles. Chaque entrée DestList devrait correspondre à un flux ; chaque flux (sauf DestList) devrait être revendiqué par une entrée. Tout ce qui est orphelin d'un côté ou de l'autre est une incohérence structurelle que les fichiers sains maintenus par le shell ne produisent pas.
L'autre mode d'échec, c'est l'édition directe des octets du DestList. Chaque entrée porte une valeur d'intégrité, et les lignes éditées à la main par des scripts qui ne la recalculent pas ne concorderont pas. Windows est laxiste à valider cela en pratique, donc le fichier falsifié se charge quand même, ce qui est tout l'intérêt pour l'attaquant, mais le mauvais checksum survit comme un drapeau pour vous. Traitez une incohérence de checksum comme suspecte, pas comme concluante, car les bugs de parseur et la divergence de version en produisent aussi.
Et puis il y a la récupération. Comme c'est un fichier composé avec une FAT, les secteurs d'un flux retiré passent en non alloué, pas à zéro. Ils restent là jusqu'à réallocation. Le carving de la chaîne de secteurs libres CFBF peut récupérer un flux LNK abandonné, ou suffisamment de l'un pour récupérer le chemin cible et le GUID de volume intégré. Ce n'est pas garanti, les secteurs sont réutilisés, mais j'ai récupéré un chemin UNC complet dans l'espace résiduel d'un fichier composé sur un fichier que l'utilisateur croyait avoir nettoyé.
Le cliché instantané gagne presque toujours
Tout ce qui précède est intéressant et la plupart du temps vous n'en avez pas besoin, parce que le Volume Shadow Copy a discrètement pris un instantané de la Jump List avant que quiconque y touche. Montez les clichés instantanés, extrayez le même .automaticDestinations-ms de chacun, et comparez les DestList entre instantanés. L'entrée qui existe dans l'instantané de mardi et manque dans celui de vendredi vous dit à la fois ce qui a été retiré et la fenêtre durant laquelle c'est arrivé. La suppression sélective sur le volume actif est entièrement déjouée par une copie dont l'utilisateur ignorait l'existence et qu'il ne peut pas atteindre.
C'est la première chose à vérifier, pas la dernière. Je commence par les internes du fichier composé parce que vous devriez les comprendre, mais opérationnellement l'ordre est : clichés instantanés, puis journal USN, puis carving, puis analyse au niveau de l'octet de ce qui a survécu.
Une chronologie que vous pouvez défendre
La reconstruction ne vaut quelque chose que si elle tient, donc deux habitudes.
Fixez votre horloge. Le FILETIME du DestList est en UTC et les dates de l'en-tête LNK sont en UTC, mais le système que vous reconstruisez a peut-être déplacé son horloge : corrections NTP, changement manuel, machine virtuelle restaurée vers un ancien instantané. Quand l'ordre des ID d'entrée et l'ordre des horodatages divergent, le déplacement de l'horloge est une explication plus courante et plus honnête que la falsification, et vous la vérifiez avant d'alléguer une manipulation. Cherchez les événements de changement d'heure dans System.evtx (l'événement ID 4616 dans Security pour "l'heure système a été modifiée") via le parseur EVTX, et réconciliez.
Ne vous appuyez pas sur un seul artefact. Une Jump List place un fichier devant un utilisateur à un moment donné. UserAssist confirme que l'application a réellement été lancée dans une session GUI. Prefetch confirme que le processus s'est exécuté. Le journal USN confirme que le fichier cible existait et a été touché. SRUM confirme que l'application tournait et poussait des octets. La Jump List est un fil solide, et c'est un fil. Les dossiers qui s'effondrent au contre-interrogatoire sont ceux bâtis sur un seul artefact lu au pied de la lettre.
Pour une revue ponctuelle sans déplacer le fichier hors du poste, déposez-le sur la page d'accueil et il décode les flux et le DestList côté client, rien n'est envoyé. Quand vous avez besoin d'une sortie par lots et d'événements de chronologie, JLECmd et Plaso restent les outils. La comparaison des outils en fait le tour.
Pour aller plus loin
- Yogesh Khatri et Mike Stevens, "Jumping into the Past", la rétro-ingénierie du DestList sur laquelle tout cela repose.
- Eric Zimmerman, JLECmd et JumpListExplorer.
- Joachim Metz, libolecf, référence pour le conteneur de fichier composé OLE si vous faites du carving à la main.
- MITRE ATT&CK T1070 Indicator Removal pour situer les Jump Lists vidées dans le tableau d'ensemble.