
Kit modulaire de réponse aux incidents pour collecter des données forensiques sur des endpoints macOS potentiellement infectés, capturant les artefacts navigateur, les mécanismes de persistance, les processus et les configurations réseau.
Cet ensemble de scripts est conçu pour collecter diverses données depuis un terminal suspecté d'être infecté, afin de faciliter le processus de réponse aux incidents. Ces données ne doivent pas être considérées comme une collecte forensique complète, mais elles capturent beaucoup d'informations forensiques utiles.
Si vous voulez de véritables données forensiques, vous devriez vraiment capturer un dump mémoire complet et imager l'intégralité du disque. Cela ne fait pas partie du périmètre de ce kit.
Le script doit être exécuté sur un système en direct, pas sur une image ou une autre source de données forensiques. Il n'exige pas strictement de permissions root pour s'exécuter, mais il ne pourra pas collecter une grande partie des données prévues sans.
Les données seront collectées sous deux formes. La première sous forme de fichiers résumé, contenant la sortie de commandes shell, des données extraites de bases de données, etc. Par exemple, le module browser produira un fichier browser_extensions.txt avec un résumé de toutes les extensions de navigateur installées pour Safari, Chrome et Firefox.
La seconde forme est constituée de fichiers complets collectés depuis le système de fichiers. Ceux-ci sont stockés dans un sous-dossier artifacts à l'intérieur du dossier de collecte.
Le script est très simple à exécuter. Il ne prend qu'un seul paramètre, obligatoire, pour passer un script de configuration au format JSON :
./pict.py -c /path/to/config.json
Le script de configuration décrit ce que le script collectera, et comment. Il devrait ressembler à ceci :
{
"collection_dest" : "~/Desktop/",
"all_users" : true,
"collectors" : {
"browser" : "BrowserExtCollector",
"persist" : "PersistenceCollector",
"suspicious" : "SuspiciousBehaviorCollector",
"browserhist" : "BrowserHistoryCollector",
"bash_config" : "BashConfigCollector",
"bash_hist" : "BashHistoryCollector",
"processes" : "ProcessCollector",
"network_config" : "NetworkConfigCollector",
"profiles" : "ProfileCollector",
"certs" : "TrustedCertCollector"
},
"settings" : {
"keepLSData" : true,
"zipIt" : true
},
"moduleSettings" : {
"browser" : {
"collectArtifacts" : true
}
},
"unused" : {
"installs" : "InstallationCollector"
}
}
Ceci spécifie le chemin pour stocker les données collectées. Il peut s'agir d'un chemin absolu ou d'un chemin relatif au dossier personnel de l'utilisateur (en commençant par un tilde). Le chemin par défaut, s'il n'est pas spécifié, est /Users/Shared.
Les données seront collectées dans un dossier créé à cet emplacement. Ce dossier aura un nom de la forme PICT-computername-YYYY-MM-DD, où le nom d'ordinateur est le nom de la machine spécifié dans Préférences Système > Partage et la date est la date de collecte.
Si vrai, collecte les données de tous les utilisateurs de la machine chaque fois que possible. Si faux, collecte les données uniquement pour l'utilisateur exécutant le script. S'il n'est pas spécifié, cette valeur par défaut est true.
PICT est modulaire et peut facilement être étendu ou réduit en périmètre, simplement en changeant les modules Collector utilisés.
Les données collectors sont un dictionnaire où la clé est le nom d'un module à charger (le nom du fichier Python sans l'extension .py) et la valeur est le nom de la sous-classe Collector trouvée dans ce module. Vous pouvez ajouter des entrées supplémentaires pour des modules personnalisés (voir Écrire vos propres modules), ou supprimer des entrées pour empêcher ces modules de s'exécuter. Une façon simple de supprimer des modules, sans avoir à rechercher les noms exacts plus tard si vous voulez les rajouter, est de les déplacer dans un dictionnaire de niveau supérieur nommé unused.
Ce dictionnaire fournit les paramètres globaux.
keepLSData spécifie si le fichier lsregister.txt - qui peut être assez volumineux - doit être conservé. (Ce fichier est généré automatiquement et est utilisé par certains autres modules pour construire leur sortie. Il contient une mine d'informations utiles, mais peut dépasser les 100 Mo. Si vous n'avez pas besoin de toutes ces données, ou ne voulez pas traiter autant de données, définissez ce paramètre sur false et il sera supprimé une fois la collecte terminée.)
zipIt spécifie s'il faut générer automatiquement un fichier zip avec le contenu du dossier de collecte. Notez que le processus de compression et décompression des données modifiera certains attributs, comme la propriété des fichiers.
Ce dictionnaire spécifie les paramètres spécifiques aux modules. Tous les modules n'ont pas leurs propres paramètres, mais si un module autorise ses propres paramètres, vous pouvez les fournir ici. Dans l'exemple ci-dessus, vous pouvez voir un paramètre booléen nommé collectArtifacts utilisé avec le module browser.
Il existe également des paramètres globaux de module qui sont gérés par la classe Collector et qui peuvent être définis individuellement pour chaque module.
collectArtifacts spécifie s'il faut collecter les artefacts de fichier qui seraient normalement collectés par le module. Si false, tous les artefacts seront omis pour ce module. Cela peut être nécessaire dans les cas où l'espace de stockage est une considération, et où les artefacts collectés sont volumineux, ou dans les cas où les artefacts collectés peuvent représenter un problème de confidentialité pour l'utilisateur dont le système est analysé.
Les modules doivent consister en un fichier contenant une classe qui hérite de Collector (défini dans collectors/collector.py), et ils doivent être placés dans le dossier collectors. Un nouveau module Collector peut être facilement créé en dupliquant le fichier collectors/template.py et en le personnalisant pour votre propre usage.
def __init__(self, collectionPath, allUsers)Cette méthode peut être surchargée si nécessaire, mais le super Collector.init() doit être appelé dans ce cas, de préférence avant l'exécution de votre code personnalisé. Cela donne à l'objet la chance de configurer ses propriétés avant que votre code tente de les utiliser.
def printStartInfo(self)C'est une méthode très simple qui sera appelée lorsque la collecte de ce module commence. Son but est d'afficher un message sur stdout pour donner à l'utilisateur une idée de la progression, en fournissant un retour sur ce qui se passe.
def applySettings(self, settingsDict)Cela donne au module la possibilité d'appliquer des paramètres personnalisés. Chaque module peut avoir ses propres paramètres définis, mais le settingsDict doit également être passé au super, afin que la classe Collection puisse gérer les paramètres qu'elle définit.
def collect(self)Cette méthode est le cœur du module. Elle est appelée lorsqu'il est temps pour le module de commencer la collecte. Elle peut écrire autant de fichiers que nécessaire, mais doit confiner cette activité aux fichiers situés dans le chemin self.collectionPath, et doit utiliser des noms de fichiers qui ne sont pas déjà utilisés par d'autres modules.
Si vous souhaitez collecter des artefacts, n'essayez pas de le faire vous-même. Ajoutez simplement des chemins au tableau self.pathsToCollect, et la classe Collector se chargera de les copier dans les sous-chemins appropriés du dossier artifacts, et de maintenir les métadonnées (permissions, attributs étendus, flags, etc.) sur les artefacts.
Lorsque la méthode se termine, assurez-vous d'appeler le super (Collector.collect(self)) pour donner à la classe Collector la chance de gérer ses responsabilités, comme la collecte des artefacts.
Votre méthode collect peut utiliser toutes les données collectées dans les fichiers basic_info.txt ou lsregister.txt situés dans self.collectionPath. Ceux-ci sont collectés au début par le script pict.py, et peuvent être considérés comme disponibles pour utilisation par tout autre module. Cependant, vous ne devez pas vous fier aux sorties des autres modules, car rien ne garantit que les fichiers seront disponibles lorsque votre module s'exécutera. Les modules peuvent ne pas s'exécuter dans l'ordre dans lequel ils apparaissent dans votre JSON de configuration, car les dictionnaires Python ne sont pas ordonnés.
Merci à Greg Neagle pour FoundationPlist.py, qui a résolu de nombreux problèmes de lecture des plists binaires, des plists contenant des types de données date, etc.