def collectMiscArtifacts(self): self.pathsToCollect.append("/var/at/jobs") self.pathsToCollect.append("/etc/security/audit_warn") self.pathsToCollect.append("/etc/rc.common") self.pathsToCollect.append("/etc/launchd.conf") for user in self.userList: userFolder = util.safePathJoin("/Users", user) self.pathsToCollect.append(util.safePathJoin(userFolder, ".launchd.conf"))
def collectSafariAppExtensions(self): lsregisterInfoPath = util.safePathJoin(self.collectionPath, "lsregister.txt") extensions = os.popen( """egrep -i -10 'NSExtensionPointIdentifier\s*=\s*"com\.apple\.Safari' {0}""" .format(lsregisterInfoPath)).read().rstrip() return extensions
def collectHiddenLoginItems(self): lsregisterInfoPath = util.safePathJoin(self.collectionPath, "lsregister.txt") logins = os.popen( "egrep -oi '(/[^/]+)*/Contents/Library/LoginItems/.+\.app' {0} | grep -v '/Volumes/.*\.backupdb/'" .format(lsregisterInfoPath)).read().rstrip() return logins
def collectUserLaunchAgents(self): userFolderPaths = [] for user in self.userList: userFolderPaths.append(util.safePathJoin("/Users", user)) output = "" collectFolders = [] for userFolder in userFolderPaths: userLaunchAgentsFolder = util.safePathJoin(userFolder, "Library/LaunchAgents") if os.path.exists(userLaunchAgentsFolder): output += userLaunchAgentsFolder + "\n" output += os.popen("ls -aleO {0}".format(userLaunchAgentsFolder)).read().rstrip() output += "\n\n" self.pathsToCollect.append(userLaunchAgentsFolder) return output.rstrip()
def getChromeExtensionList(self): extensionsDict = {} for user in self.userList: extensionsForUser = {} profileList = util.getChromeProfilesForUser(user) # Iterate over all profiles for profile in profileList: extensionsForProfile = [] extFolder = util.safePathJoin(profileList[profile], "Extensions") if not os.path.exists(extFolder): continue extensionList = os.listdir(extFolder) if not extensionList: continue # Iterate extensions for profile for extension in extensionList: extPath = util.safePathJoin(extFolder, extension) if not os.path.isdir(extPath): continue manifestPath = "" folderList = os.listdir(extPath) versFolderPath = "" for folder in folderList: versFolderPath = util.safePathJoin(extPath, folder) manifestPath = util.safePathJoin( versFolderPath, "manifest.json") if os.path.isfile(manifestPath): break if manifestPath == "": continue # A manifest.json file was found, this must be a valid extension extensionsForProfile.append(extPath) #end for extension extensionsForUser[profile] = extensionsForProfile #end for profile extensionsDict[user] = extensionsForUser #end for user return extensionsDict
def getFirefoxExtensionList(self): extensionsDict = {} for user in self.userList: extensionsForUser = {} profileList = util.getFirefoxProfilesForUser(user) # Iterate over all profiles for profile in profileList: extensionsForProfile = [] extFolder = util.safePathJoin(profileList[profile], "extensions") if not os.path.exists(extFolder): continue extensionList = os.listdir(extFolder) if not extensionList: continue # Iterate extensions for profile for extension in extensionList: if extension == ".DS_Store": continue extPath = util.safePathJoin(extFolder, extension) if os.path.isdir(extPath): # Older extension, is there an install.rdf? installrdfPath = util.safePathJoin( extPath, "install.rdf") if not os.path.isfile(installrdfPath): continue else: # Is it a .xpi? if not extension.endswith(".xpi"): continue # If we're still here, this must be a valid extension extensionsForProfile.append(extPath) #end for extension extensionsForUser[profile] = extensionsForProfile #end for profile extensionsDict[user] = extensionsForUser #end for user return extensionsDict
def collect(self): filename = "persistence.txt" filePath = util.safePathJoin(self.collectionPath, filename) f = open(filePath, "w+") f.write( "PICT - Persistence information\n==============================\n\n" ) f.write("Login items (current user)\n------------------------------\n") logins = self.collectLoginItems() f.write(logins + "\n\n") f.write("Hidden login items\n------------------------------\n") logins = self.collectHiddenLoginItems() f.write(logins + "\n\n") f.write("Kernel extensions\n------------------------------\n") output = self.collectKexts() f.write(output + "\n\n") f.write("Cron jobs\n------------------------------\n") output = self.collectCronJobs() f.write(output + "\n\n") f.write("Startup items\n------------------------------\n") output = self.collectStartupItems() f.write(output + "\n\n") f.write("Login hooks\n------------------------------\n") output = self.collectLoginHooks() f.write(output + "\n\n") f.write("Launch agents\n------------------------------\n") output = self.collectLaunchAgents() f.write(output + "\n\n") f.write("Launch daemons\n------------------------------\n") output = self.collectLaunchDaemons() f.write(output + "\n\n") f.write("User launch agents\n------------------------------\n") output = self.collectUserLaunchAgents() f.write(output + "\n\n") f.write("launchctl list\n------------------------------\n") output = self.collectLaunchctlList() f.write(output + "\n\n") f.close self.collectMiscArtifacts() Collector.collect(self)
def collectSafari(self): try: os.listdir(os.path.expanduser("~/Library/Safari/Extensions")) except Exception as e: if "Operation not permitted" in str(e): return "Unable to access due to TCC restrictions" output = "" extensionsForUsers = self.getSafariExtensionList() for user in extensionsForUsers: output += "Extensions for {0}:\n".format(user) extensionList = extensionsForUsers[user] # Iterate extensions for user for extPath in extensionList: # Mark this path for collection self.pathsToCollect.append(extPath) tempdir = tempfile.mkdtemp() err = os.system("cd {0}; xar -xf {1}".format(tempdir, extPath)) if err: extName = "Unknown file format ({0})".format(err) else: # Expand the extension tempdirlist = os.listdir(tempdir) for oneItem in tempdirlist: oneItemPath = util.safePathJoin(tempdir, oneItem) if os.path.isdir(oneItemPath): expExtPath = oneItemPath break # Read the name from the Info.plist extInfoPlistPath = util.safePathJoin( expExtPath, "Info.plist") extInfoPlist = plistlib.readPlist(extInfoPlistPath) extName = extInfoPlist["CFBundleDisplayName"] # end if output += " Extension: {0}\n".format(extPath) output += " Name: {0}\n\n".format(extName) return output.rstrip()
def getSafariExtensionList(self): extensionsDict = {} for user in self.userList: extensionsForUser = [] safariExtFolder = "/Users/{0}/Library/Safari/Extensions".format( user) if not os.path.isdir(safariExtFolder): continue safariExtList = os.listdir(safariExtFolder) for extension in safariExtList: if extension.endswith(".safariextz"): extensionsFolderPath = util.safePathJoin( safariExtFolder, extension) extensionsForUser.append(extensionsFolderPath) if extensionsForUser: extensionsDict[user] = extensionsForUser return extensionsDict
def collectFirefox(self): output = "" for user in self.userList: output += "History for user {0}:\n\n".format(user) profileList = util.getFirefoxProfilesForUser(user) for profile in profileList: output += " > History for profile {0}:\n\n".format(profile) profilePath = profileList[profile] dbFilePath = util.safePathJoin(profilePath, "places.sqlite") if os.path.isfile(dbFilePath): self.pathsToCollect.append(dbFilePath) else: output += "No history found\n\n" continue query = "select strftime('%Y-%m-%d %H:%M:%S', datetime((moz_historyvisits.visit_date / 1000000), 'unixepoch'), 'utc'), moz_places.url from moz_historyvisits, moz_places where moz_historyvisits.place_id=moz_places.id and moz_historyvisits.visit_date/1000000 > (strftime('%s', 'now') - 2592000*3) order by moz_historyvisits.visit_date;" output += self.collectForQuery(dbFilePath, query) output += "\n\n" return output.rstrip()
def collectChrome(self): output = "" for user in self.userList: output += "History for user {0}:\n\n".format(user) profileList = util.getChromeProfilesForUser(user) for profile in profileList: output += " > History for profile {0}:\n\n".format(profile) profilePath = profileList[profile] dbFilePath = util.safePathJoin(profilePath, "History") if os.path.isfile(dbFilePath): self.pathsToCollect.append(dbFilePath) else: output += "No history found\n\n" continue query = "select strftime('%Y-%m-%d %H:%M:%S', datetime((strftime('%s','1601-01-01 00:00:00') + (visits.visit_time / 1000000)), 'unixepoch'), 'utc'), urls.url from visits, urls where visits.url=urls.id and visits.visit_time > (strftime('%s', 'now') - strftime('%s', '1601-01-01 00:00:00') - 2592000*3) order by visits.visit_time;" output += self.collectForQuery(dbFilePath, query) output += "\n\n" return output.rstrip()
def collectFileList(self, pathList, collectionDir): if not self.collectArtifacts: return if not pathList: return if not collectionDir: return if collectionDir == "" or not os.path.isdir(collectionDir): return artifactsFolder = util.safePathJoin(collectionDir, self.artifactsFolderName) for onePath in pathList: if os.path.exists(onePath): try: thisDestPath = os.path.join(artifactsFolder, onePath.lstrip("/")) # Use subprocess.call to ensure paths are properly escaped err = subprocess.call(["ditto", onePath, thisDestPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if err: print "Error collecting path: {0}".format(onePath) print "error = {0}".format(err) except Exception as e: print "Error collecting path: {0}".format(onePath) print e
def collectChrome(self): output = "" extensionsForUsers = self.getChromeExtensionList() for user in extensionsForUsers: output += "Extensions for {0}:\n".format(user) extensionsForProfiles = extensionsForUsers[user] # Iterate over all profiles for profile in extensionsForProfiles: output += " Profile {0}:\n".format(profile) extensionList = extensionsForProfiles[profile] # Iterate extensions for profile for extPath in extensionList: if not os.path.isdir(extPath): continue manifestPath = "" folderList = os.listdir(extPath) versFolderPath = "" for folder in folderList: versFolderPath = util.safePathJoin(extPath, folder) manifestPath = util.safePathJoin( versFolderPath, "manifest.json") if os.path.isfile(manifestPath): break if manifestPath == "": continue # A manifest.json file was found, this must be a valid extension # Mark this path for collection self.pathsToCollect.append(extPath) f = open(manifestPath, "r") manifestJSON = f.read() f.close manifestDict = json.loads(manifestJSON) extName = manifestDict['name'] if extName.startswith("__MSG_") and extName.endswith("__"): # There's a localized name, look for it in English localePath = util.safePathJoin( versFolderPath, "_locales/en/messages.json") if not os.path.exists(localePath): localePath = util.safePathJoin( versFolderPath, "_locales/en_US/messages.json") if not os.path.exists(localePath): extName = "Unknown name" else: nameKey = extName[6:][:-2] f = open(localePath, "r") localeJSON = f.read() f.close localeDict = json.loads(localeJSON) extName = dict( (key.lower(), value) for key, value in localeDict.iteritems())[ nameKey.lower()]['message'] #end if extName.startswith("__MSG_") output += " Extension: {0}\n".format(extPath) output += " Name: {0}\n\n".format(extName) #end for extension #end for profile #end for user return output.rstrip()
def collectFirefox(self): output = "" extensionsForUsers = self.getFirefoxExtensionList() for user in extensionsForUsers: output += "Extensions for {0}:\n".format(user) extensionsForProfiles = extensionsForUsers[user] # Iterate over all profiles for profile in extensionsForProfiles: output += " Profile {0}:\n".format(profile) extensionList = extensionsForProfiles[profile] # Iterate extensions for profile for extPath in extensionList: if extPath.endswith(".DS_Store"): continue # Mark this path for collection self.pathsToCollect.append(extPath) if os.path.isdir(extPath): # Older extension, look for name in install.rdf installrdfPath = util.safePathJoin( extPath, "install.rdf") if not os.path.isfile(installrdfPath): extName = "Unknown extension type" else: f = open(installrdfPath, "r") installrdfData = f.read() f.close m = re.search("<em:name>([^<]*)</em:name>", installrdfData, re.I) if m: extName = m.group(1) else: extName = "Unknown name" else: # Is it a .xpi? If so, decompress and get name if extPath.endswith(".xpi"): tempdir = tempfile.mkdtemp() zipf = open(extPath, "rb") zip = zipfile.ZipFile(zipf) zip.extract("manifest.json", tempdir) zipf.close manifestPath = util.safePathJoin( tempdir, "manifest.json") f = open(manifestPath, "r") manifestJSON = f.read() f.close manifestDict = json.loads(manifestJSON) extName = dict((key.lower(), value) for key, value in manifestDict.iteritems())['name'] else: extName = "Unknown extension type" output += " Extension: {0}\n".format(extPath) output += " Name: {0}\n\n".format(extName) #end for extension #end for profile #end for user return output.rstrip()
def collect(self): basicsFilename = "basic_info.txt" basicsFilePath = util.safePathJoin(self.collectionPath, basicsFilename) f = open(basicsFilePath, "w+") user = os.popen("logname").read().rstrip() when = datetime.utcnow().strftime("%-d %b %Y @ %H:%M:%S UTC") whenlocal = datetime.now().strftime("%-d %b %Y @ %H:%M:%S") f.write("Collected by user {0} on {1} (local {2})\n".format( user, when, whenlocal)) uptime = os.popen("uptime").read().rstrip() f.write("Uptime: {0}\n".format(uptime)) hostname = os.popen("hostname").read().rstrip() f.write("Hostname: {0}\n".format(hostname)) spctlStatus = os.popen("spctl --status").read().rstrip() f.write("System policy security: {0}\n".format(spctlStatus)) if spctlStatus != "assessments enabled": f.write(" Gatekeeper is disabled!\n") sipStatus = os.popen("csrutil status").read().rstrip() + "\n" f.write(sipStatus) filevaultStatus = os.popen("fdesetup status").read().rstrip() f.write("FileVault status: {0}\n".format(filevaultStatus)) firewallStatus = os.popen( "defaults read /Library/Preferences/com.apple.alf globalstate" ).read().rstrip() if firewallStatus == "0": f.write("Application firewall is not enabled\n") else: f.write("Application firewall is enabled\n") # Concerning things... if os.path.exists("/private/etc/kcpassword"): f.write("WARNING! Automatic login is enabled by user!\n") result = os.system( "egrep -i '127\.0\.0\.1\s*.*activate.*\.adobe\.com' /Users/thomas/Desktop/hosts &>/dev/null" ) if result == 0: f.write("WARNING! Hosts file shows signs of piracy activity!\n") # System profiler stuff f.write("\n") systemOverview = os.popen( "system_profiler SPSoftwareDataType").read().rstrip() f.write(systemOverview) f.write("\n\n") hardwareOverview = os.popen( "system_profiler SPHardwareDataType").read().rstrip() f.write(hardwareOverview) f.write("\n\n") # Users f.write("User list\n-----------------------------\n") users = os.popen("dscl . list /Users | grep -v '_'").read().rstrip() userlist = users.split("\n") for user in userlist: if user != "daemon" and user != "nobody": userInfo = os.popen("dscacheutil -q user -a name {0}".format( user)).read().rstrip() f.write(userInfo + "\n\n") f.write("Admin users\n-----------------------------\n") users = os.popen( "dscl . -read /Groups/admin GroupMembership").read().rstrip() userlist = users.split()[1:] for user in userlist: f.write(user + "\n") f.write("\n") # Login activity f.write("Users logged in\n-----------------------------\n") users = os.popen("w").read().rstrip() f.write(users + "\n\n") f.write("Last logins\n-----------------------------\n") users = os.popen("last").read().rstrip() f.write(users + "\n\n") f.close Collector.collect(self)