Forensique des téléchargements macOS : un cas pas à pas
Une intrusion macOS synthétique analysée avec Quarantine Parser : chronologie, ligne supprimée récupérée, rejet Gatekeeper et AppleDouble sur USB.
TL;DR. Ce cas pas à pas utilise l’échantillon synthétique du site : l’ordinateur portable macOS FIN-MBP-03, l’utilisatrice dana.whitlock, le 14 septembre 2026. En quelques minutes, les données de quarantaine révèlent une archive d’outils venue d’un hébergeur de fichiers, dont la ligne en base a été supprimée puis récupérée, un téléchargement depuis un site de paste, un faux LaunchAgent portant la valeur de quarantaine de l’archive, un assistant dont la quarantaine a été retirée, une image disque dont Gatekeeper a rejeté l’app, et un tableur financier qui a atterri sur un volume USB. Il montre aussi où s’arrêtent les données de quarantaine.
Tout ce qui suit est fictif : machines, personnes et fichiers n’existent pas, et chaque domaine se termine par .example.
Le scénario
Le cas prolonge l’intrusion fictive FIN-WKS-07 / svc_backup. Un attaquant disposant d’une session volée est actif sur le portable de dana.whitlock, au service financier, entre environ 10:05 et 10:50 UTC. La question : qu’est-ce qui a été téléchargé, qu’est-ce qui a été préparé, et quelque chose est-il sorti ?
1. Charger l’échantillon
Ouvrez la page d’accueil sur /fr et cliquez sur Essayer un exemple. L’analyse se fait dans votre navigateur ; rien n’est envoyé. La vue Sources liste ce qui a été chargé et comment chaque fichier a été analysé. La période de référence couvre du 18 août au 15 septembre : rapports de l’intranet et du serveur de documents enregistrés avec Safari, MeetClient-5.2.pkg et MeetClient-5.3.pkg depuis downloads.meet.example avec Chrome (la 5.3 approuvée par l’utilisatrice, flags 00c3), pièces jointes Mail de ap@vendor.example et de priya.raman@fin.example, pièces jointes Messages et un AirDrop reçu via sharingd.
2. Définir la fenêtre de l’incident
Réglez la plage horaire sur 10:00–10:55 UTC le 2026-09-14, en la saisissant ou en faisant glisser sur la bande de densité. La plage s’applique à toutes les vues et à tous les exports, et elle est conservée dans le hash de l’URL : le lien reproduit votre vue. La période de référence disparaît et la vue Constats ne liste plus que les pistes de cette fenêtre.
3. Lire la chronologie
La Chronologie montre trois téléchargements dans la fenêtre :
- 10:07:12, Safari,
https://files.example/s/8f3k2/tools.zip, originehttps://files.example/s/8f3k2; - 10:12:05, Chrome,
https://paste.example/raw/Zx81; - 10:26:30, Safari,
https://transfer.example/d/Qm7/sync-agent.dmg.
La vue Domaines ne montre aucun de ces hôtes dans la période de référence. Les pistes les signalent comme hébergeurs de fichiers, de paste et de transfert, et signalent les archives et images disque provenant de domaines rarement vus. Ce sont des pistes, pas des verdicts : le personnel financier utilise bel et bien des services de transfert.
4. La ligne files.example récupérée
La ligne tools.zip est marquée comme récupérée. Dans le scénario, elle a été supprimée de la base avec sqlite3 vers 10:48. Le lecteur SQLite propre à l’outil a retrouvé l’enregistrement dans l’espace libre de la page de table et l’a reconstruit. La piste indique « suppression ou effacement possible ». L’article sur la récupération explique le carving.
La ligne QuarantineEventsV2 récupérée se joint toujours par UUID : la vue Fichiers montre cinq fichiers portant son identifiant, dont les trois fichiers extraits par l’Utilitaire d’archive (tools/README.txt, tools/updater.sh, tools/com.example.updater.plist) avec la même valeur de quarantaine. Supprimer la ligne n’a pas retiré les attributs, et ce sont les attributs qui remettent la ligne en contexte.
5. Le texte de paste.example
Le téléchargement Chrome aboutit à ~/Downloads/Zx81.txt. Un paste brut récupéré pendant une intrusion peut être un script ou une liste de commandes, mais les données de quarantaine ne prouvent que le téléchargement. Collectez et lisez le fichier lui-même.
6. La copie dans LaunchAgents
Une copie de com.example.updater.plist se trouve dans ~/Library/LaunchAgents/. Elle porte toujours la valeur de quarantaine de tools.zip, UUID compris : c’est l’un des cinq fichiers joints. La piste « fichier téléchargé dans un dossier de démarrage automatique » la désigne. La valeur de quarantaine indique d’où vient le plist, pas quand il a été copié dans LaunchAgents ; datez cette étape avec d’autres artefacts.
7. Le sync-helper dépouillé
~/Library/Caches/.sync/sync-helper a un WhereFroms qui pointe vers files.example, mais aucun attribut de quarantaine. L’outil lève « WhereFroms sans attribut de quarantaine (xattr -d possible) ». Dans le scénario, l’attribut a été retiré. Dans un cas réel, pesez les autres explications présentées dans kMDItemWhereFroms face à la quarantaine avant de parler d’anti-forensique.
8. L’image disque de transfer.example et le rejet Gatekeeper
Trente-cinq secondes après le téléchargement de l’image disque, un enregistrement .LastGKReject montre que Gatekeeper a refusé /Volumes/Sync Agent/SyncAgent.app à 10:27:05, avec XProtectMalwareType 2 (rapporté comme app/programme non signé ; la correspondance n’est pas documentée par Apple). Le nom du volume correspond à l’image ; le lien repose sur le nom et l’heure. Voir .LastGKReject comme preuve pour la chaîne complète.
9. Q3_forecast.xlsx sur le volume EXFIL
Pour cette étape, élargissez la plage à l’ensemble des données. Q3_forecast.xlsx était une pièce jointe Mail reçue le 28 août. L’échantillon inclut son fichier AppleDouble ._Q3_forecast.xlsx provenant du volume USB exFAT EXFIL. Ce fichier contient encore la valeur de quarantaine de Mail et le WhereFroms, ce qui relie la copie présente sur la clé à la pièce jointe d’origine.
Chronologie (UTC, 2026-09-14)
| Heure | Source | Événement |
|---|---|---|
| 10:07:12 | Ligne récupérée, cinq fichiers joints | Safari télécharge tools.zip depuis files.example |
| 10:12:05 | Ligne en base, attribut | Chrome télécharge Zx81.txt depuis paste.example |
| non daté | Attribut de la copie LaunchAgent | com.example.updater.plist dans ~/Library/LaunchAgents/ |
| non daté | WhereFroms, sans quarantaine | sync-helper sous ~/Library/Caches/.sync/ |
| 10:26:30 | Ligne en base | Safari télécharge sync-agent.dmg depuis transfer.example |
| 10:27:05 | .LastGKReject | Gatekeeper rejette SyncAgent.app, type 2 |
| vers 10:48 | Récupération dans l’espace libre | Ligne files.example supprimée avec sqlite3 |
| non daté | AppleDouble sur EXFIL | Q3_forecast.xlsx présent sur le volume USB |
Exportez la chronologie et les fichiers en CSV, en JSON ou en CSV prêt pour Timesketch ; les exports suivent la plage sélectionnée.
Ce que les données de quarantaine ne montrent pas
Trois étapes du scénario ne laissent aucune trace de quarantaine :
- le lancement de Terminal utilisé pour exécuter des commandes ;
- l’octroi de l’accès complet au disque dans TCC ;
- la copie sur USB elle-même.
Pivotez vers d’autres artefacts : les journaux unifiés pour l’activité des processus et des politiques de sécurité, TCC.db pour les autorisations de confidentialité, et FSEvents pour les modifications du système de fichiers, comme l’écriture sur le volume EXFIL. Collectez aussi les fichiers eux-mêmes : Zx81.txt, updater.sh, le plist du LaunchAgent et sync-helper.
Pour les commandes de collecte, voir collecter les événements de quarantaine, et pour une référence condensée, la fiche sur les événements de quarantaine de notre site partenaire.
Questions fréquentes
Le cas FIN-MBP-03 est-il réel ?
Non. Machine, utilisateur, domaines et fichiers sont fictifs et les données sont synthétiques. Tous les domaines utilisent le suffixe réservé .example. L’échantillon sert à montrer comment les artefacts s’articulent.
Les données de quarantaine peuvent-elles montrer qu’un fichier a été copié sur USB ?
Pas directement. Elles montrent d’où vient un fichier. Dans l’échantillon, le fichier AppleDouble du volume EXFIL montre que Q3_forecast.xlsx a atteint ce volume avec ses attributs, mais la copie elle-même doit être datée avec d’autres artefacts, comme FSEvents.
Pourquoi la ligne files.example manquait-elle dans la base active ?
Dans le scénario, elle a été supprimée avec sqlite3 vers 10:48 UTC. Quarantine Parser l’a récupérée en analysant l’espace libre de la page de table. La récupération dépend des réglages SQLite et des écritures ultérieures : une absence de récupération n’aurait donc rien prouvé.