Aller au contenu

Accès aux fichiersRéseauExécution

QuarantineEventsV2 et xattr de quarantaine sous macOS

QuarantineEventsV2 et l'attribut com.apple.quarantine indiquent quoi a été téléchargé, depuis quelle URL, par quelle app et quand, même après suppression.

Emplacement
~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2
Prouve
Quels fichiers un utilisateur a téléchargés, depuis quelle URL et quelle page d'origine, avec quelle application, et quand
Horodatages
Base : temps absolu Mac (UTC) ; xattr : secondes Unix en hexadécimal (UTC)
Accès
Utilisateur propriétaire ou root ; accès complet au disque recommandé pour l'outil de collecte
Rétention
Jusqu'à suppression ou effacement (aucune purge automatique documentée)
Collecte
Aftermath, mac_apt, UAC, Velociraptor

Ce que c'est

Deux artefacts liés enregistrent les téléchargements sous macOS. Lorsqu'une application qui adhère à la mise en quarantaine des fichiers (navigateurs, Mail, Messages, AirDrop, applications sandboxées) écrit un fichier, macOS lui attache l'attribut étendu com.apple.quarantine. Launch Services écrit aussi une ligne dans une base SQLite propre à l'utilisateur, QuarantineEventsV2, qui contient l'URL de téléchargement, la page d'origine et l'agent de téléchargement.

Le xattr voyage avec le fichier ; la ligne de la base reste en place quand le fichier est supprimé. Ensemble, ils constituent le moyen standard de répondre à la question « d'où vient ce fichier ? ».

Où il se trouve

ArtefactCheminRemarques
Base de quarantaine~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2Une par utilisateur, SQLite, table unique LSQuarantineEvent
xattr de quarantainecom.apple.quarantine sur le fichier lui-mêmeStocké dans des fichiers AppleDouble ._ sur les volumes sans xattr natifs
Dernier rejet Gatekeeper/private/var/db/SystemPolicyConfiguration/.LastGKReject sur les macOS actuels ; /private/var/db/.LastGKReject sur les versions plus anciennesÉcrit par syspolicyd ; mac_apt QUARANTINE ne cherche que l'ancien chemin dans une image, donnez-lui directement le fichier actuel

La base a conservé ce nom et cet emplacement sur les versions modernes de macOS. Elle est lisible par son propriétaire ; collectez-la en root pour tous les utilisateurs, avec l'accès complet au disque accordé à l'outil de collecte.

Ce qu'il prouve

  • Qu'une URL a été téléchargée par un agent nommé (com.apple.Safari, com.google.Chrome, com.apple.mail) à un moment donné.
  • La page qui a mené au téléchargement (LSQuarantineOriginURLString) et, pour le courrier, l'expéditeur.
  • Qu'un fichier précis sur disque provient d'un téléchargement précis (l'UUID du xattr est égal à LSQuarantineEventIdentifier).
  • Si un fichier en quarantaine a été ouvert et approuvé via Gatekeeper (drapeau d'approbation utilisateur dans le xattr).

Elle n'enregistre pas le chemin d'enregistrement local ni le nom du fichier, et ne couvre pas les téléchargements effectués avec curl, wget, scp, git ou d'autres outils qui n'adhèrent pas au mécanisme. L'absence d'enregistrement est une preuve faible.

Champs clés

Table LSQuarantineEvent :

ColonneSignification
LSQuarantineEventIdentifierUUID (clé primaire), correspond au dernier champ du xattr
LSQuarantineTimeStampHeure de l'événement, temps absolu Mac (REAL)
LSQuarantineAgentBundleIdentifierBundle ID de l'application de téléchargement
LSQuarantineAgentNameNom affiché de l'agent
LSQuarantineDataURLStringURL des données téléchargées
LSQuarantineOriginURLStringPage référente ou d'origine
LSQuarantineOriginTitleTitre de l'origine, lorsqu'il est enregistré
LSQuarantineSenderName, LSQuarantineSenderAddressExpéditeur, renseigné par des agents comme Mail
LSQuarantineTypeNumberType de quarantaine Launch Services (téléchargement web, pièce jointe, etc.)
LSQuarantineOriginAliasBlob d'alias, en général vide

Valeur du xattr flags;timestamp;agent;UUID, par exemple 0083;66f7c2a1;Safari;<UUID>. Les bits de drapeaux documentés par les chercheurs (Apple ne les documente pas publiquement) incluent 0x0001 téléchargement, 0x0002 sandbox, 0x0004 quarantaine stricte et 0x0040 approuvé par l'utilisateur.

Horodatages

  • LSQuarantineTimeStamp : temps absolu Mac, secondes depuis le 2001-01-01 00:00:00 UTC. Ajoutez 978307200 pour obtenir un temps Unix.
  • Horodatage du xattr : secondes Unix en hexadécimal, UTC.
SELECT datetime(LSQuarantineTimeStamp + 978307200, 'unixepoch') AS event_utc,
       LSQuarantineAgentBundleIdentifier, LSQuarantineDataURLString,
       LSQuarantineOriginURLString, LSQuarantineEventIdentifier
FROM LSQuarantineEvent ORDER BY LSQuarantineTimeStamp;
xattr -p com.apple.quarantine ./sample.dmg
date -u -r $((16#66f7c2a1))

Rétention

Aucune purge automatique n'est documentée : les enregistrements s'accumulent en général pendant toute la vie du profil utilisateur, ce qui les rend précieux une fois le fichier téléchargé supprimé. Les utilisateurs, les outils de nettoyage et les attaquants peuvent supprimer des lignes avec sqlite3, ou supprimer le fichier entier. Le xattr est perdu lorsqu'il est retiré avec xattr -d/xattr -c, lorsqu'un fichier est copié vers un système de fichiers ou un service qui abandonne les attributs étendus, ou lorsqu'une archive est extraite par un outil qui ne propage pas la quarantaine.

Collecte

# Every user's database (root, collector with Full Disk Access)
for u in /Users/*; do
  f="$u/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2"
  [ -f "$f" ] || continue
  d="./case/qe_$(basename "$u")"
  mkdir -p "$d" && sudo cp -p "$f"* "$d/"
done
# Preserve xattrs when copying suspect files
ditto --extattr ./suspect.pkg ./case/files/
  • Aftermath collecte la base LSQuarantine.
  • UAC : files/system/quarantine_events.yaml (base par utilisateur).
  • Velociraptor : MacOS.System.QuarantineEvents.
  • Analyse à froid : copiez depuis le volume Data monté ; calculez d'abord les empreintes, travaillez sur des copies.

Analyse

  • sqlite3 avec la requête ci-dessus.
  • Plugin QUARANTINE de mac_apt (base et .LastGKReject) : python mac_apt.py -o out MOUNTED /Volumes/evidence QUARANTINE.
  • Plugin ls_quarantine de Plaso : log2timeline.py --parsers 'sqlite/ls_quarantine' --storage-file qe.plaso ./case.
  • Relier un fichier à son enregistrement :
SELECT * FROM LSQuarantineEvent
WHERE LSQuarantineEventIdentifier = '<UUID from xattr>';

Quarantine Parser lit dans le navigateur QuarantineEventsV2 avec son -wal (et récupère les lignes supprimées), les listings de l'xattr com.apple.quarantine et .LastGKReject, et relie chaque fichier sur disque à son enregistrement de téléchargement ; rien n'est envoyé.

Conseils d'investigation

  • Un binaire suspect sans xattr de quarantaine mais avec un enregistrement correspondant dans la base suggère que l'attribut a été retiré délibérément.
  • Une seconde charge récupérée avec curl ne laisse aucune trace de quarantaine ; poursuivez vers l'historique du shell, les Unified Logs et FSEvents.
  • Rapprochez les URL de données de l'historique Safari ou de Chrome/Firefox, et de kMDItemWhereFroms dans le magasin Spotlight.
  • Le passage du drapeau de 0083 à 00c3 active 0x0040 (approuvé par l'utilisateur) : l'élément a été ouvert et autorisé. Traitez la signification des drapeaux comme un élément de preuve complémentaire.
  • Des trous dans une chronologie par ailleurs dense, ou une base bien plus petite que ne le laisserait penser l'historique du navigateur, peuvent indiquer un effacement manuel ; vérifiez le WAL et les pages libres.
  • Confondre les époques du xattr (Unix, hexadécimal) et de la base (temps absolu Mac) produit des erreurs de 31 ans.
  • Pour les décisions Gatekeeper qui suivent le téléchargement, voir Gatekeeper et XProtect et le guide quarantaine et Gatekeeper.

Voir aussi